#include "settings/MediaSettings.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 7
+#define NUM_CROP_PIX 3
#define ARSIZE(x) (sizeof(x) / sizeof((x)[0]))
-CVDPAU::Desc decoder_profiles[] = {
+// settings codecs mapping
+DVDCodecAvailableType g_vdpau_available[] = {
+ { AV_CODEC_ID_H263, "videoplayer.usevdpaumpeg4" },
+ { AV_CODEC_ID_MPEG4, "videoplayer.usevdpaumpeg4" },
+ { AV_CODEC_ID_WMV3, "videoplayer.usevdpauvc1" },
+ { AV_CODEC_ID_VC1, "videoplayer.usevdpauvc1" },
+ { AV_CODEC_ID_MPEG2VIDEO, "videoplayer.usevdpaumpeg2" },
+};
+const size_t settings_count = sizeof(g_vdpau_available) / sizeof(DVDCodecAvailableType);
+
+CDecoder::Desc decoder_profiles[] = {
{"MPEG1", VDP_DECODER_PROFILE_MPEG1},
{"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE},
{"MPEG2_MAIN", VDP_DECODER_PROFILE_MPEG2_MAIN},
{"VC1_SIMPLE", VDP_DECODER_PROFILE_VC1_SIMPLE},
{"VC1_MAIN", VDP_DECODER_PROFILE_VC1_MAIN},
{"VC1_ADVANCED", VDP_DECODER_PROFILE_VC1_ADVANCED},
-#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
{"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP},
-#endif
-};
-const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CVDPAU::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}
};
+const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CDecoder::Desc);
static struct SInterlaceMapping
{
, {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;
+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}
-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;
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
- m_glPixmap = 0;
- m_Pixmap = 0;
- if (!glXBindTexImageEXT)
- glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXBindTexImageEXT");
- if (!glXReleaseTexImageEXT)
- glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXReleaseTexImageEXT");
-
- 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;
- past[0] = NULL;
- past[1] = NULL;
- current = NULL;
- future = NULL;
- tmpNoiseReduction = 0.0f;
- tmpSharpness = 0.0f;
- vdp_get_proc_address = NULL;
- vdp_device_destroy = NULL;
- vdp_video_surface_create = NULL;
- vdp_video_surface_destroy = NULL;
- vdp_video_surface_put_bits_y_cb_cr = NULL;
- vdp_video_surface_get_bits_y_cb_cr = NULL;
- vdp_output_surface_put_bits_y_cb_cr = NULL;
- vdp_output_surface_put_bits_native = NULL;
- vdp_output_surface_create = NULL;
- vdp_output_surface_destroy = NULL;
- vdp_output_surface_get_bits_native = NULL;
- vdp_output_surface_render_output_surface = NULL;
- vdp_output_surface_put_bits_indexed = NULL;
- vdp_video_mixer_create = NULL;
- vdp_video_mixer_set_feature_enables = NULL;
- vdp_video_mixer_query_parameter_support = NULL;
- vdp_video_mixer_query_feature_support = NULL;
- vdp_video_mixer_destroy = NULL;
- vdp_video_mixer_render = NULL;
- m_hwContext.bitstream_buffers_allocated = 0;
-}
+CVDPAUContext *CVDPAUContext::m_context = 0;
+CCriticalSection CVDPAUContext::m_section;
+Display *CVDPAUContext::m_display = 0;
+void *CVDPAUContext::m_dlHandle = 0;
-bool CVDPAU::Open(AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces)
+CVDPAUContext::CVDPAUContext()
{
- if(avctx->coded_width == 0
- || avctx->coded_height == 0)
- {
- CLog::Log(LOGWARNING,"(VDPAU) no width/height available, can't init");
- return false;
- }
+ m_context = 0;
+ m_refCount = 0;
+}
- if ((avctx->codec_id == AV_CODEC_ID_MPEG4) && !g_advancedSettings.m_videoAllowMpeg4VDPAU)
- return false;
+void CVDPAUContext::Release()
+{
+ CSingleLock lock(m_section);
- if (!dl_handle)
+ m_refCount--;
+ if (m_refCount <= 0)
{
- dl_handle = dlopen("libvdpau.so.1", RTLD_LAZY);
- if (!dl_handle)
- {
- const char* error = dlerror();
- if (!error)
- 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;
- }
+ Close();
+ delete this;
+ m_context = 0;
}
+}
- if (!m_dllAvUtil.Load())
- return false;
+void CVDPAUContext::Close()
+{
+ CLog::Log(LOGNOTICE, "VDPAU::Close - closing decoder context");
+ DestroyContext();
+}
- InitVDPAUProcs();
+bool CVDPAUContext::EnsureContext(CVDPAUContext **ctx)
+{
+ CSingleLock lock(m_section);
- if (vdp_device != VDP_INVALID_HANDLE)
+ if (m_context)
{
- SpewHardwareAvailable();
+ m_context->m_refCount++;
+ *ctx = m_context;
+ return true;
+ }
- VdpDecoderProfile profile = 0;
- if(avctx->codec_id == AV_CODEC_ID_H264)
- profile = VDP_DECODER_PROFILE_H264_HIGH;
-#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
- else if(avctx->codec_id == AV_CODEC_ID_MPEG4)
- profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
-#endif
- if(profile)
+ m_context = new CVDPAUContext();
+ *ctx = m_context;
+ {
+ CSingleLock gLock(g_graphicsContext);
+ if (!m_context->LoadSymbols() || !m_context->CreateContext())
{
- if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
- CLog::Log(LOGWARNING,"(VDPAU) width %i might not be supported because of hardware bug", avctx->width);
-
- /* 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);
-
- 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);
- FiniVDPAUProcs();
- return false;
- }
-
- vdp_decoder_destroy(decoder);
- CheckStatus(vdp_st, __LINE__);
+ delete m_context;
+ m_context = 0;
+ *ctx = NULL;
+ return false;
}
+ }
- InitCSCMatrix(avctx->coded_height);
-
- /* finally setup ffmpeg */
- memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
- m_hwContext.render = CVDPAU::Render;
- m_hwContext.bitstream_buffers_allocated = 0;
- avctx->get_buffer = CVDPAU::FFGetBuffer;
- avctx->release_buffer = CVDPAU::FFReleaseBuffer;
- avctx->draw_horiz_band = CVDPAU::FFDrawSlice;
- avctx->slice_flags=SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
- avctx->hwaccel_context = &m_hwContext;
- avctx->thread_count = 1;
+ m_context->m_refCount++;
- g_Windowing.Register(this);
- return true;
- }
- return false;
+ *ctx = m_context;
+ return true;
}
-CVDPAU::~CVDPAU()
+VDPAU_procs& CVDPAUContext::GetProcs()
{
- Close();
+ return m_vdpProcs;
}
-void CVDPAU::Close()
+VdpVideoMixerFeature* CVDPAUContext::GetFeatures()
{
- CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
+ return m_vdpFeatures;
+}
- FiniVDPAUOutput();
- FiniVDPAUProcs();
+int CVDPAUContext::GetFeatureCount()
+{
+ return m_featureCount;
+}
- while (!m_videoSurfaces.empty())
+bool CVDPAUContext::LoadSymbols()
+{
+ if (!m_dlHandle)
{
- vdpau_render_state *render = m_videoSurfaces.back();
- m_videoSurfaces.pop_back();
- if (render->bitstream_buffers_allocated)
- m_dllAvUtil.av_freep(&render->bitstream_buffers);
- render->bitstream_buffers_allocated = 0;
- free(render);
+ m_dlHandle = dlopen("libvdpau.so.1", RTLD_LAZY);
+ if (!m_dlHandle)
+ {
+ const char* error = dlerror();
+ if (!error)
+ error = "dlerror() returned NULL";
+
+ CLog::Log(LOGERROR,"VDPAU::LoadSymbols: Unable to get handle to lib: %s", error);
+ return false;
+ }
}
- if (m_hwContext.bitstream_buffers_allocated)
+ char* error;
+ (void)dlerror();
+ dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(m_dlHandle, (const char*)"vdp_device_create_x11");
+ error = dlerror();
+ if (error)
{
- m_dllAvUtil.av_freep(&m_hwContext.bitstream_buffers);
+ CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
+ m_vdpDevice = VDP_INVALID_HANDLE;
+ return false;
}
-
- g_Windowing.Unregister(this);
- m_dllAvUtil.Unload();
+ return true;
}
-bool CVDPAU::MakePixmapGL()
-{
- 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;
+bool CVDPAUContext::CreateContext()
+{
+ CLog::Log(LOGNOTICE,"VDPAU::CreateContext - creating decoder context");
+
+ int mScreen;
+ { CSingleLock lock(g_graphicsContext);
+ if (!m_display)
+ m_display = XOpenDisplay(NULL);
+ mScreen = g_Windowing.GetCurrentScreen();
}
- 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);
+ VdpStatus vdp_st;
+ // Create Device
+ vdp_st = dl_vdp_device_create_x11(m_display,
+ mScreen,
+ &m_vdpDevice,
+ &m_vdpProcs.vdp_get_proc_address);
- if (!m_glPixmap)
+ CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",m_vdpDevice,vdp_st);
+ if (vdp_st != VDP_STATUS_OK)
{
- CLog::Log(LOGINFO, "GLX Error: Could not create Pixmap");
- XFree(fbConfigs);
+ CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x. Falling back.",vdp_st);
+ m_vdpDevice = VDP_INVALID_HANDLE;
return false;
}
- XFree(fbConfigs);
+ QueryProcs();
+ SpewHardwareAvailable();
return true;
}
-bool CVDPAU::MakePixmap(int width, int height)
+void CVDPAUContext::QueryProcs()
{
- //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)
- {
- //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);
- }
- 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);
- }
- }
- 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;
- }
+ VdpStatus vdp_st;
- 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);
+#define VDP_PROC(id, proc) \
+ do { \
+ vdp_st = m_vdpProcs.vdp_get_proc_address(m_vdpDevice, id, (void**)&proc); \
+ if (vdp_st != VDP_STATUS_OK) \
+ { \
+ CLog::Log(LOGERROR, "CVDPAUContext::GetProcs - failed to get proc id"); \
+ } \
+ } while(0);
- if(!MakePixmapGL())
- return false;
+ VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING , m_vdpProcs.vdp_get_error_string);
+ VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY , m_vdpProcs.vdp_device_destroy);
+ VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX , m_vdpProcs.vdp_generate_csc_matrix);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE , m_vdpProcs.vdp_video_surface_create);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY , m_vdpProcs.vdp_video_surface_destroy);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR , m_vdpProcs.vdp_video_surface_put_bits_y_cb_cr);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR , m_vdpProcs.vdp_video_surface_get_bits_y_cb_cr);
+ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR , m_vdpProcs.vdp_output_surface_put_bits_y_cb_cr);
+ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE , m_vdpProcs.vdp_output_surface_put_bits_native);
+ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE , m_vdpProcs.vdp_output_surface_create);
+ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY , m_vdpProcs.vdp_output_surface_destroy);
+ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE , m_vdpProcs.vdp_output_surface_get_bits_native);
+ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpProcs.vdp_output_surface_render_output_surface);
+ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED , m_vdpProcs.vdp_output_surface_put_bits_indexed);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE , m_vdpProcs.vdp_video_mixer_create);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES , m_vdpProcs.vdp_video_mixer_set_feature_enables);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY , m_vdpProcs.vdp_video_mixer_destroy);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER , m_vdpProcs.vdp_video_mixer_render);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES , m_vdpProcs.vdp_video_mixer_set_attribute_values);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpProcs.vdp_video_mixer_query_parameter_support);
+ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT , m_vdpProcs.vdp_video_mixer_query_feature_support);
+ VDP_PROC(VDP_FUNC_ID_DECODER_CREATE , m_vdpProcs.vdp_decoder_create);
+ VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY , m_vdpProcs.vdp_decoder_destroy);
+ VDP_PROC(VDP_FUNC_ID_DECODER_RENDER , m_vdpProcs.vdp_decoder_render);
+ VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES , m_vdpProcs.vdp_decoder_query_caps);
+#undef VDP_PROC
+}
- return true;
+VdpDevice CVDPAUContext::GetDevice()
+{
+ return m_vdpDevice;
}
-void CVDPAU::BindPixmap()
+void CVDPAUContext::DestroyContext()
{
- CSharedLock lock(m_DecoderSection);
+ if (!m_vdpProcs.vdp_device_destroy)
+ return;
- { CSharedLock dLock(m_DisplaySection);
- if (m_DisplayState != VDPAU_OPEN)
- return;
- }
+ m_vdpProcs.vdp_device_destroy(m_vdpDevice);
+ m_vdpDevice = VDP_INVALID_HANDLE;
+}
- if (m_glPixmap)
+void CVDPAUContext::SpewHardwareAvailable() //CopyrighVDPAUt (c) 2008 Wladimir J. van der Laan -- VDPInfo
+{
+ VdpStatus rv;
+ CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:");
+ CLog::Log(LOGNOTICE,"name level macbs width height");
+ CLog::Log(LOGNOTICE,"------------------------------------");
+ for(unsigned int x=0; x<decoder_profile_count; ++x)
{
- if(presentSurface != VDP_INVALID_HANDLE)
+ VdpBool is_supported = false;
+ uint32_t max_level, max_macroblocks, max_width, max_height;
+ rv = m_vdpProcs.vdp_decoder_query_caps(m_vdpDevice, decoder_profiles[x].id,
+ &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
+ if(rv == VDP_STATUS_OK && is_supported)
{
- 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__);
- }
+ CLog::Log(LOGNOTICE,"%-16s %2i %5i %5i %5i\n", decoder_profiles[x].name,
+ max_level, max_macroblocks, max_width, max_height);
}
-
- glXBindTexImageEXT(m_Display, m_glPixmap, GLX_FRONT_LEFT_EXT, NULL);
}
- else CLog::Log(LOGERROR,"(VDPAU) BindPixmap called without valid pixmap");
+ CLog::Log(LOGNOTICE,"------------------------------------");
+ m_featureCount = 0;
+#define CHECK_SUPPORT(feature) \
+ do { \
+ VdpBool supported; \
+ if(m_vdpProcs.vdp_video_mixer_query_feature_support(m_vdpDevice, feature, &supported) == VDP_STATUS_OK && supported) { \
+ CLog::Log(LOGNOTICE, "Mixer feature: "#feature); \
+ m_vdpFeatures[m_featureCount++] = feature; \
+ } \
+ } while(false)
+
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
+#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
+ CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
+#endif
+#undef CHECK_SUPPORT
}
-void CVDPAU::ReleasePixmap()
+bool CVDPAUContext::Supports(VdpVideoMixerFeature feature)
{
- CSharedLock lock(m_DecoderSection);
-
- { CSharedLock dLock(m_DisplaySection);
- if (m_DisplayState != VDPAU_OPEN)
- return;
- }
-
- if (m_glPixmap)
+ for(int i = 0; i < m_featureCount; i++)
{
- glXReleaseTexImageEXT(m_Display, m_glPixmap, GLX_FRONT_LEFT_EXT);
+ if(m_vdpFeatures[i] == feature)
+ return true;
}
- else CLog::Log(LOGERROR,"(VDPAU) ReleasePixmap called without valid pixmap");
+ return false;
}
-void CVDPAU::OnLostDevice()
-{
- CLog::Log(LOGNOTICE,"CVDPAU::OnLostDevice event");
+//-----------------------------------------------------------------------------
+// VDPAU Video Surface states
+//-----------------------------------------------------------------------------
- CExclusiveLock lock(m_DecoderSection);
- FiniVDPAUOutput();
- FiniVDPAUProcs();
+#define SURFACE_USED_FOR_REFERENCE 0x01
+#define SURFACE_USED_FOR_RENDER 0x02
- m_DisplayState = VDPAU_LOST;
- m_DisplayEvent.Reset();
+void CVideoSurfaces::AddSurface(VdpVideoSurface surf)
+{
+ CSingleLock lock(m_section);
+ m_state[surf] = SURFACE_USED_FOR_REFERENCE;
}
-void CVDPAU::OnResetDevice()
+void CVideoSurfaces::ClearReference(VdpVideoSurface surf)
{
- CLog::Log(LOGNOTICE,"CVDPAU::OnResetDevice event");
-
- CExclusiveLock lock(m_DisplaySection);
- if (m_DisplayState == VDPAU_LOST)
+ CSingleLock lock(m_section);
+ if (m_state.find(surf) == m_state.end())
{
- m_DisplayState = VDPAU_RESET;
- m_DisplayEvent.Set();
+ CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
+ return;
+ }
+ m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
+ if (m_state[surf] == 0)
+ {
+ m_freeSurfaces.push_back(surf);
}
}
-int CVDPAU::Check(AVCodecContext* avctx)
+bool CVideoSurfaces::MarkRender(VdpVideoSurface surf)
{
- EDisplayState state;
+ CSingleLock lock(m_section);
+ if (m_state.find(surf) == m_state.end())
+ {
+ CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
+ return false;
+ }
+ std::list<VdpVideoSurface>::iterator it;
+ it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
+ if (it != m_freeSurfaces.end())
+ {
+ m_freeSurfaces.erase(it);
+ }
+ m_state[surf] |= SURFACE_USED_FOR_RENDER;
+ return true;
+}
- { CSharedLock lock(m_DisplaySection);
- state = m_DisplayState;
+void CVideoSurfaces::ClearRender(VdpVideoSurface surf)
+{
+ CSingleLock lock(m_section);
+ if (m_state.find(surf) == m_state.end())
+ {
+ CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
+ return;
+ }
+ m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
+ if (m_state[surf] == 0)
+ {
+ m_freeSurfaces.push_back(surf);
}
+}
- if (state == VDPAU_LOST)
+bool CVideoSurfaces::IsValid(VdpVideoSurface surf)
+{
+ CSingleLock lock(m_section);
+ if (m_state.find(surf) != m_state.end())
+ return true;
+ else
+ return false;
+}
+
+VdpVideoSurface CVideoSurfaces::GetFree(VdpVideoSurface surf)
+{
+ CSingleLock lock(m_section);
+ if (m_state.find(surf) != m_state.end())
{
- CLog::Log(LOGNOTICE,"CVDPAU::Check waiting for display reset event");
- if (!m_DisplayEvent.WaitMSec(2000))
+ std::list<VdpVideoSurface>::iterator it;
+ it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
+ if (it == m_freeSurfaces.end())
{
- CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
- state = VDPAU_RESET;
+ CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
}
else
{
- CSharedLock lock(m_DisplaySection);
- state = m_DisplayState;
+ m_freeSurfaces.erase(it);
+ m_state[surf] = SURFACE_USED_FOR_REFERENCE;
+ return surf;
}
}
- if (state == VDPAU_RESET || state == VDPAU_ERROR)
+
+ if (!m_freeSurfaces.empty())
{
- CLog::Log(LOGNOTICE,"Attempting recovery");
+ VdpVideoSurface freeSurf = m_freeSurfaces.front();
+ m_freeSurfaces.pop_front();
+ m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
+ return freeSurf;
+ }
- CSingleLock gLock(g_graphicsContext);
- CExclusiveLock lock(m_DecoderSection);
+ return VDP_INVALID_HANDLE;
+}
- FiniVDPAUOutput();
- FiniVDPAUProcs();
+VdpVideoSurface CVideoSurfaces::GetAtIndex(int idx)
+{
+ if (idx >= m_state.size())
+ return VDP_INVALID_HANDLE;
- InitVDPAUProcs();
+ std::map<VdpVideoSurface, int>::iterator it = m_state.begin();
+ for(int i = 0; i < idx; i++)
+ ++it;
+ return it->first;
+}
- if (state == VDPAU_RESET)
- return VC_FLUSHED;
- else
- return VC_ERROR;
- }
- return 0;
+VdpVideoSurface CVideoSurfaces::RemoveNext(bool skiprender)
+{
+ CSingleLock lock(m_section);
+ VdpVideoSurface surf;
+ std::map<VdpVideoSurface, int>::iterator it;
+ for(it = m_state.begin(); it != m_state.end(); ++it)
+ {
+ if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
+ continue;
+ surf = it->first;
+ m_state.erase(surf);
+
+ std::list<VdpVideoSurface>::iterator it2;
+ it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
+ if (it2 != m_freeSurfaces.end())
+ m_freeSurfaces.erase(it2);
+ return surf;
+ }
+ return VDP_INVALID_HANDLE;
}
-bool CVDPAU::IsVDPAUFormat(PixelFormat format)
+void CVideoSurfaces::Reset()
{
- if (format == AV_PIX_FMT_VDPAU)
- return true;
- else
- return false;
+ CSingleLock lock(m_section);
+ m_freeSurfaces.clear();
+ m_state.clear();
}
-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_video_mixer_create(vdp_device,
- m_feature_count,
- m_features,
- ARSIZE(parameters),
- parameters,
- parameter_values,
- &videoMixer);
- CheckStatus(vdp_st, __LINE__);
+int CVideoSurfaces::Size()
+{
+ CSingleLock lock(m_section);
+ return m_state.size();
+}
- SetHWUpscaling();
- }
+//-----------------------------------------------------------------------------
+// CVDPAU
+//-----------------------------------------------------------------------------
- if (tmpBrightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness ||
- tmpContrast != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)
- {
- SetColor();
- tmpBrightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
- tmpContrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
- }
- if (tmpNoiseReduction != CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
- {
- tmpNoiseReduction = CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction;
- SetNoiseReduction();
- }
- if (tmpSharpness != CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
- {
- tmpSharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
- SetSharpness();
- }
- if ( tmpDeintMode != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode ||
- tmpDeintGUI != CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod ||
- (tmpDeintGUI == VS_INTERLACEMETHOD_AUTO && tmpDeint != AutoInterlaceMethod()))
- {
- tmpDeintMode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
- tmpDeintGUI = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
- if (tmpDeintGUI == VS_INTERLACEMETHOD_AUTO)
- tmpDeint = AutoInterlaceMethod();
- else
- tmpDeint = tmpDeintGUI;
+CDecoder::CDecoder() : m_vdpauOutput(&m_inMsgEvent)
+{
+ m_vdpauConfig.videoSurfaces = &m_videoSurfaces;
- SetDeinterlacing();
- }
+ m_vdpauConfigured = false;
+ m_hwContext.bitstream_buffers_allocated = 0;
+ m_DisplayState = VDPAU_OPEN;
+ m_vdpauConfig.context = 0;
}
-bool CVDPAU::Supports(VdpVideoMixerFeature feature)
+bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat fmt, unsigned int surfaces)
{
- for(int i = 0; i < m_feature_count; i++)
+ // check if user wants to decode this format with VDPAU
+ std::string gpuvendor = g_Windowing.GetRenderVendor();
+ std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
+ // nvidia is whitelisted despite for mpeg-4 we need to query user settings
+ if ((gpuvendor.compare(0, 6, "nvidia") != 0) || (avctx->codec_id == AV_CODEC_ID_MPEG4) || (avctx->codec_id == AV_CODEC_ID_H263))
{
- if(m_features[i] == feature)
- return true;
+ if (CDVDVideoCodec::IsCodecDisabled(g_vdpau_available, settings_count, avctx->codec_id))
+ return false;
}
- return false;
-}
-bool CVDPAU::Supports(EINTERLACEMETHOD method)
-{
- if(method == VS_INTERLACEMETHOD_VDPAU_BOB
- || method == VS_INTERLACEMETHOD_AUTO
- || method == VS_INTERLACEMETHOD_AUTO_ION)
- return true;
+#ifndef GL_NV_vdpau_interop
+ CLog::Log(LOGNOTICE, "VDPAU: compilation without required extension GL_NV_vdpau_interop");
+ return false;
+#endif
+ if (!g_Windowing.IsExtSupported("GL_NV_vdpau_interop"))
+ {
+ CLog::Log(LOGNOTICE, "VDPAU::Open: required extension GL_NV_vdpau_interop not found");
+ return false;
+ }
- for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
+ if(avctx->coded_width == 0
+ || avctx->coded_height == 0)
{
- if(p->method == method)
- return Supports(p->feature);
+ CLog::Log(LOGWARNING,"VDPAU::Open: no width/height available, can't init");
+ return false;
}
- return false;
-}
+ m_vdpauConfig.numRenderBuffers = surfaces;
+ m_decoderThread = CThread::GetCurrentThreadId();
-EINTERLACEMETHOD CVDPAU::AutoInterlaceMethod()
-{
- return VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
-}
+ if (!CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
+ return false;
-void CVDPAU::SetColor()
-{
- VdpStatus vdp_st;
+ m_DisplayState = VDPAU_OPEN;
+ m_vdpauConfigured = false;
- if (tmpBrightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)
- m_Procamp.brightness = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)-50) / 100;
- if (tmpContrast != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)
- m_Procamp.contrast = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)+50) / 100;
+ if (!m_dllAvUtil.Load())
+ return false;
- 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);
+ m_presentPicture = 0;
- VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
- if (CSettings::Get().GetBool("videoscreen.limitedrange"))
- {
- 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__);
-}
+ VdpDecoderProfile profile = 0;
-void CVDPAU::SetNoiseReduction()
-{
- if(!Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
- return;
+ // convert FFMPEG codec ID to VDPAU profile.
+ ReadFormatOf(avctx->codec_id, profile, m_vdpauConfig.vdpChromaType);
+ if(profile)
+ {
+ VdpStatus vdp_st;
+ VdpBool is_supported = false;
+ uint32_t max_level, max_macroblocks, max_width, max_height;
- VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION };
- VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL };
- VdpStatus vdp_st;
+ // query device capabilities to ensure that VDPAU can handle the requested codec
+ vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_query_caps(m_vdpauConfig.context->GetDevice(),
+ profile, &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
- if (!CMediaSettings::Get().GetCurrentVideoSettings().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[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction };
- CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction);
- vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, nr);
- CheckStatus(vdp_st, __LINE__);
-}
+ // test to make sure there is a possibility the codec will work
+ if (CheckStatus(vdp_st, __LINE__))
+ {
+ CLog::Log(LOGERROR, "VDPAU::Open: error %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
+ return false;
+ }
-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 (max_width < avctx->coded_width || max_height < avctx->coded_height)
+ {
+ CLog::Log(LOGWARNING,"VDPAU::Open: requested picture dimensions (%i, %i) exceed hardware capabilities ( %i, %i).",
+ avctx->coded_width, avctx->coded_height, max_width, max_height);
+ return false;
+ }
- if (!CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
- {
- VdpBool enabled[]={0};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
- CheckStatus(vdp_st, __LINE__);
- return;
+ if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
+ CLog::Log(LOGWARNING,"VDPAU::Open width %i might not be supported because of hardware bug", avctx->width);
+
+ // attempt to create a decoder with this width/height, some sizes are not supported by hw
+ vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(), profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder);
+
+ if (CheckStatus(vdp_st, __LINE__))
+ {
+ CLog::Log(LOGERROR, "VDPAU::Open: error: %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
+ return false;
+ }
+
+ m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
+ CheckStatus(vdp_st, __LINE__);
+
+ // finally setup ffmpeg
+ memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
+ m_hwContext.render = CDecoder::Render;
+ m_hwContext.bitstream_buffers_allocated = 0;
+ avctx->get_buffer = CDecoder::FFGetBuffer;
+ avctx->reget_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;
+ avctx->hwaccel_context = &m_hwContext;
+
+ g_Windowing.Register(this);
+ return true;
+ }
}
- VdpBool enabled[]={1};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
- CheckStatus(vdp_st, __LINE__);
- void* sh[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness };
- CLog::Log(LOGNOTICE,"Setting Sharpness to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness);
- vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, sh);
- CheckStatus(vdp_st, __LINE__);
+ return false;
}
-void CVDPAU::SetHWUpscaling()
+CDecoder::~CDecoder()
{
-#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
+ Close();
}
-void CVDPAU::SetDeinterlacing()
+void CDecoder::Close()
{
- VdpStatus vdp_st;
- EDEINTERLACEMODE mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
- EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
- if (method == VS_INTERLACEMETHOD_AUTO)
- method = AutoInterlaceMethod();
+ CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
- 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)
+ g_Windowing.Unregister(this);
+
+ CSingleLock lock(m_DecoderSection);
+
+ FiniVDPAUOutput();
+ m_vdpauOutput.Dispose();
+
+ if (m_hwContext.bitstream_buffers_allocated)
{
- VdpBool enabled[]={0,0,0};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+ m_dllAvUtil.av_freep(&m_hwContext.bitstream_buffers);
}
- else
+
+ m_dllAvUtil.Unload();
+
+ if (m_vdpauConfig.context)
+ m_vdpauConfig.context->Release();
+ m_vdpauConfig.context = 0;
+}
+
+long CDecoder::Release()
+{
+ // check if we should do some pre-cleanup here
+ // a second decoder might need resources
+ if (m_vdpauConfigured == true)
{
- if (method == VS_INTERLACEMETHOD_AUTO_ION)
+ CSingleLock lock(m_DecoderSection);
+ CLog::Log(LOGNOTICE,"CVDPAU::Release pre-cleanup");
+
+ Message *reply;
+ if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
+ &reply,
+ 2000))
{
- 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)
+ bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
{
- VdpBool enabled[]={1,0,0};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+ CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup returned error", __FUNCTION__);
+ m_DisplayState = VDPAU_ERROR;
}
}
- else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
+ else
{
- VdpBool enabled[]={1,0,0};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+ CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup timed out", __FUNCTION__);
+ m_DisplayState = VDPAU_ERROR;
}
- else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
+
+ VdpVideoSurface surf;
+ while((surf = m_videoSurfaces.RemoveNext(true)) != VDP_INVALID_HANDLE)
{
- VdpBool enabled[]={1,1,0};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+ m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
}
- else if (method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
+ }
+ return IHardwareDecoder::Release();
+}
+
+long CDecoder::ReleasePicReference()
+{
+ return IHardwareDecoder::Release();
+}
+
+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 || 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())
{
- VdpBool enabled[]={1,0,1};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+ m_vdpauConfig.outWidth = g_graphicsContext.GetWidth();
+ m_vdpauConfig.outHeight = MathUtils::round_int((double)height * g_graphicsContext.GetWidth() / width);
}
- else
+ else //scale height to the desktop size if the aspect ratio is smaller than the desktop
{
- VdpBool enabled[]={0,0,0};
- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+ m_vdpauConfig.outHeight = g_graphicsContext.GetHeight();
+ m_vdpauConfig.outWidth = MathUtils::round_int((double)width * g_graphicsContext.GetHeight() / height);
}
}
-
- CheckStatus(vdp_st, __LINE__);
+ else
+ { //let opengl scale
+ m_vdpauConfig.outWidth = width;
+ m_vdpauConfig.outHeight = height;
+ }
+ CLog::Log(LOGDEBUG, "CVDPAU::SetWidthHeight Setting OutWidth: %i OutHeight: %i", m_vdpauConfig.outWidth, m_vdpauConfig.outHeight);
}
-void CVDPAU::InitVDPAUProcs()
+void CDecoder::OnLostDevice()
{
- char* error;
+ CLog::Log(LOGNOTICE,"CVDPAU::OnLostDevice event");
- (void)dlerror();
- dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(dl_handle, (const char*)"vdp_device_create_x11");
- error = dlerror();
- if (error)
- {
- CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
- vdp_device = VDP_INVALID_HANDLE;
+ int count = g_graphicsContext.exit();
- //g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Error, "VDPAU", error, 10000);
+ CSingleLock lock(m_DecoderSection);
+ FiniVDPAUOutput();
+ if (m_vdpauConfig.context)
+ m_vdpauConfig.context->Release();
+ m_vdpauConfig.context = 0;
- return;
- }
+ m_DisplayState = VDPAU_LOST;
+ lock.Leave();
+ m_DisplayEvent.Reset();
- if (dl_vdp_device_create_x11)
- {
- CSingleLock lock(g_graphicsContext);
- m_Display = g_Windowing.GetDisplay();
- }
- else
+ g_graphicsContext.restore(count);
+}
+
+void CDecoder::OnResetDevice()
+{
+ CLog::Log(LOGNOTICE,"CVDPAU::OnResetDevice event");
+
+ int count = g_graphicsContext.exit();
+
+ CSingleLock lock(m_DecoderSection);
+ if (m_DisplayState == VDPAU_LOST)
{
- CLog::Log(LOGERROR,"(VDPAU) - Unable to get dl_vdp_device_create_x11 in %s", __FUNCTION__);
- vdp_device = VDP_INVALID_HANDLE;
- return;
+ m_DisplayState = VDPAU_RESET;
+ lock.Leave();
+ m_DisplayEvent.Set();
}
- int mScreen = DefaultScreen(m_Display);
- VdpStatus vdp_st;
+ g_graphicsContext.restore(count);
+}
- // 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);
-
- CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",vdp_device,vdp_st);
- if (vdp_st != VDP_STATUS_OK)
+int CDecoder::Check(AVCodecContext* avctx)
+{
+ EDisplayState state;
+
+ { CSingleLock lock(m_DecoderSection);
+ state = m_DisplayState;
+ }
+
+ if (state == VDPAU_LOST)
{
- CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x. Falling back.",vdp_st);
- vdp_device = VDP_INVALID_HANDLE;
- return;
+ CLog::Log(LOGNOTICE,"CVDPAU::Check waiting for display reset event");
+ if (!m_DisplayEvent.WaitMSec(4000))
+ {
+ CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
+ state = VDPAU_RESET;
+ }
+ else
+ {
+ CSingleLock lock(m_DecoderSection);
+ state = m_DisplayState;
+ }
}
+ if (state == VDPAU_RESET || state == VDPAU_ERROR)
+ {
+ CSingleLock lock(m_DecoderSection);
-#define VDP_PROC(id, proc) \
- do { \
- vdp_st = vdp_get_proc_address(vdp_device, id, (void**)&proc); \
- CheckStatus(vdp_st, __LINE__); \
- } while(0);
+ FiniVDPAUOutput();
+ if (m_vdpauConfig.context)
+ m_vdpauConfig.context->Release();
+ m_vdpauConfig.context = 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);
-
-#undef VDP_PROC
+ if (CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
+ {
+ m_DisplayState = VDPAU_OPEN;
+ m_vdpauConfigured = false;
+ }
- // 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;
+ if (state == VDPAU_RESET)
+ return VC_FLUSHED;
+ else
+ return VC_ERROR;
+ }
+ return 0;
+}
- m_vdpauOutputMethod = OUTPUT_NONE;
+bool CDecoder::IsVDPAUFormat(PixelFormat format)
+{
+ if (format == AV_PIX_FMT_VDPAU)
+ return true;
+ else
+ return false;
+}
- CExclusiveLock lock(m_DisplaySection);
- m_DisplayState = VDPAU_OPEN;
- vdpauConfigured = false;
+bool CDecoder::Supports(VdpVideoMixerFeature feature)
+{
+ return m_vdpauConfig.context->Supports(feature);
}
-void CVDPAU::FiniVDPAUProcs()
+bool CDecoder::Supports(EINTERLACEMETHOD method)
{
- if (vdp_device == VDP_INVALID_HANDLE) return;
+ if(method == VS_INTERLACEMETHOD_VDPAU_BOB
+ || method == VS_INTERLACEMETHOD_AUTO)
+ return true;
- VdpStatus vdp_st;
- vdp_st = vdp_device_destroy(vdp_device);
- CheckStatus(vdp_st, __LINE__);
- vdp_device = VDP_INVALID_HANDLE;
- vdpauConfigured = false;
+ if (method == VS_INTERLACEMETHOD_RENDER_BOB)
+ return true;
+
+ if (method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
+ return false;
+
+ for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
+ {
+ if(p->method == method)
+ return Supports(p->feature);
+ }
+ return false;
}
-void CVDPAU::InitCSCMatrix(int Height)
+EINTERLACEMETHOD CDecoder::AutoInterlaceMethod()
{
- 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__);
+ return VS_INTERLACEMETHOD_RENDER_BOB;
}
-void CVDPAU::FiniVDPAUOutput()
+void CDecoder::FiniVDPAUOutput()
{
- FiniOutputMethod();
-
- if (vdp_device == VDP_INVALID_HANDLE || !vdpauConfigured) return;
+ if (!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.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
if (CheckStatus(vdp_st, __LINE__))
return;
- decoder = VDP_INVALID_HANDLE;
+ m_vdpauConfig.vdpDecoder = VDP_INVALID_HANDLE;
- for (unsigned int i = 0; i < m_videoSurfaces.size(); ++i)
+ CLog::Log(LOGDEBUG, "CVDPAU::FiniVDPAUOutput destroying %d video surfaces", m_videoSurfaces.Size());
+
+ VdpVideoSurface surf;
+ while((surf = m_videoSurfaces.RemoveNext()) != VDP_INVALID_HANDLE)
{
- vdpau_render_state *render = m_videoSurfaces[i];
- if (render->surface != VDP_INVALID_HANDLE)
- {
- vdp_st = vdp_video_surface_destroy(render->surface);
- render->surface = VDP_INVALID_HANDLE;
- }
+ m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
if (CheckStatus(vdp_st, __LINE__))
return;
}
+ m_videoSurfaces.Reset();
}
-
-void CVDPAU::ReadFormatOf( AVCodecID codec
- , VdpDecoderProfile &vdp_decoder_profile
- , VdpChromaType &vdp_chroma_type)
+void CDecoder::ReadFormatOf( AVCodecID codec
+ , VdpDecoderProfile &vdp_decoder_profile
+ , VdpChromaType &vdp_chroma_type)
{
switch (codec)
{
vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
vdp_chroma_type = VDP_CHROMA_TYPE_420;
break;
- case AV_CODEC_ID_H264:
+ case AV_CODEC_ID_H264:
vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
vdp_chroma_type = VDP_CHROMA_TYPE_420;
break;
vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
vdp_chroma_type = VDP_CHROMA_TYPE_420;
break;
-#if (defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP)
case AV_CODEC_ID_MPEG4:
vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
vdp_chroma_type = VDP_CHROMA_TYPE_420;
break;
-#endif
default:
vdp_decoder_profile = 0;
vdp_chroma_type = 0;
}
}
-
-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->codec_id, 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->codec_id, vdp_decoder_profile, m_vdpauConfig.vdpChromaType);
if(avctx->codec_id == AV_CODEC_ID_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.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(),
vdp_decoder_profile,
- surface_width,
- surface_height,
- max_references,
- &decoder);
+ m_vdpauConfig.surfaceWidth,
+ m_vdpauConfig.surfaceHeight,
+ m_vdpauConfig.maxReferences,
+ &m_vdpauConfig.vdpDecoder);
if (CheckStatus(vdp_st, __LINE__))
return false;
- m_vdpauOutputMethod = OUTPUT_NONE;
+ // 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;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "VDPAU::%s - failed to init output", __FUNCTION__);
+ m_vdpauOutput.Dispose();
+ return false;
+ }
- vdpauConfigured = true;
+ m_inMsgEvent.Reset();
+ m_vdpauConfigured = true;
+ m_ErrorCount = 0;
return true;
}
-bool CVDPAU::ConfigOutputMethod(AVCodecContext *avctx, AVFrame *pFrame)
+int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic)
{
- VdpStatus vdp_st;
+ //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
+ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
+ CDecoder* vdp = (CDecoder*)ctx->GetHardware();
- if (m_vdpauOutputMethod == OUTPUT_PIXMAP)
- return true;
+ // while we are waiting to recover we can't do anything
+ CSingleLock lock(vdp->m_DecoderSection);
- FiniOutputMethod();
+ if(vdp->m_DisplayState != VDPAU_OPEN)
+ {
+ CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
+ return -1;
+ }
- MakePixmap(avctx->width,avctx->height);
+ VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
+ surf = vdp->m_videoSurfaces.GetFree(surf != 0 ? surf : VDP_INVALID_HANDLE);
- vdp_st = vdp_presentation_queue_target_create_x11(vdp_device,
- m_Pixmap, //x_window,
- &vdp_flip_target);
- if (CheckStatus(vdp_st, __LINE__))
- return false;
+ VdpStatus vdp_st = VDP_STATUS_ERROR;
+ if (surf == VDP_INVALID_HANDLE)
+ {
+ // create a new surface
+ VdpDecoderProfile profile;
+ ReadFormatOf(avctx->codec_id, profile, vdp->m_vdpauConfig.vdpChromaType);
- vdp_st = vdp_presentation_queue_create(vdp_device,
- vdp_flip_target,
- &vdp_flip_queue);
- if (CheckStatus(vdp_st, __LINE__))
- return false;
+ vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_video_surface_create(vdp->m_vdpauConfig.context->GetDevice(),
+ vdp->m_vdpauConfig.vdpChromaType,
+ avctx->coded_width,
+ avctx->coded_height,
+ &surf);
+ vdp->CheckStatus(vdp_st, __LINE__);
+ if (vdp_st != VDP_STATUS_OK)
+ {
+ CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
+ return -1;
+ }
+ vdp->m_videoSurfaces.AddSurface(surf);
+ }
- totalAvailableOutputSurfaces = 0;
+ pic->data[1] = pic->data[2] = NULL;
+ pic->data[0] = (uint8_t*)(uintptr_t)surf;
+ pic->data[3] = (uint8_t*)(uintptr_t)surf;
- int tmpMaxOutputSurfaces = NUM_OUTPUT_SURFACES;
- if (vid_width == FULLHD_WIDTH)
- tmpMaxOutputSurfaces = NUM_OUTPUT_SURFACES_FOR_FULLHD;
+ pic->linesize[0] = pic->linesize[1] = pic->linesize[2] = 0;
- // 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__))
- return false;
- totalAvailableOutputSurfaces++;
- }
- CLog::Log(LOGNOTICE, " (VDPAU) Total Output Surfaces Available: %i of a max (tmp: %i const: %i)",
- totalAvailableOutputSurfaces,
- tmpMaxOutputSurfaces,
- NUM_OUTPUT_SURFACES);
+ pic->type= FF_BUFFER_TYPE_USER;
+
+ pic->reordered_opaque= avctx->reordered_opaque;
+ return 0;
+}
+
+void CDecoder::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic)
+{
+ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
+ CDecoder* vdp = (CDecoder*)ctx->GetHardware();
- // 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));
+ VdpVideoSurface surf;
+ unsigned int i;
- surfaceNum = presentSurfaceNum = 0;
- outputSurface = presentSurface = VDP_INVALID_HANDLE;
- videoMixer = VDP_INVALID_HANDLE;
+ CSingleLock lock(vdp->m_DecoderSection);
- m_vdpauOutputMethod = OUTPUT_PIXMAP;
+ surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
- return true;
+ vdp->m_videoSurfaces.ClearReference(surf);
+
+ for(i=0; i<4; i++)
+ pic->data[i]= NULL;
}
-bool CVDPAU::FiniOutputMethod()
+VdpStatus CDecoder::Render( VdpDecoder decoder, VdpVideoSurface target,
+ VdpPictureInfo const *picture_info,
+ uint32_t bitstream_buffer_count,
+ VdpBitstreamBuffer const * bitstream_buffers)
{
- VdpStatus vdp_st;
+ return VDP_STATUS_OK;
+}
+
+void CDecoder::FFDrawSlice(struct AVCodecContext *s,
+ const AVFrame *src, int offset[4],
+ int y, int type, int height)
+{
+ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)s->opaque;
+ CDecoder* vdp = (CDecoder*)ctx->GetHardware();
+
+ // while we are waiting to recover we can't do anything
+ CSingleLock lock(vdp->m_DecoderSection);
+
+ if(vdp->m_DisplayState != VDPAU_OPEN)
+ return;
- if (vdp_flip_queue != VDP_INVALID_HANDLE)
+ if(src->linesize[0] || src->linesize[1] || src->linesize[2]
+ || offset[0] || offset[1] || offset[2])
{
- vdp_st = vdp_presentation_queue_destroy(vdp_flip_queue);
- vdp_flip_queue = VDP_INVALID_HANDLE;
- CheckStatus(vdp_st, __LINE__);
+ CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
+ return;
}
- if (vdp_flip_target != VDP_INVALID_HANDLE)
+ VdpStatus vdp_st;
+ VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)src->data[3];
+
+ // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
+ if (!vdp->m_videoSurfaces.IsValid(surf))
{
- vdp_st = vdp_presentation_queue_target_destroy(vdp_flip_target);
- vdp_flip_target = VDP_INVALID_HANDLE;
- CheckStatus(vdp_st, __LINE__);
+ CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
+ return;
}
- if (m_glPixmap)
+ uint32_t max_refs = 0;
+ if(s->codec_id == AV_CODEC_ID_H264)
+ max_refs = vdp->m_hwContext.info.h264.num_ref_frames;
+
+ if(vdp->m_vdpauConfig.vdpDecoder == VDP_INVALID_HANDLE
+ || vdp->m_vdpauConfigured == false
+ || vdp->m_vdpauConfig.maxReferences < max_refs)
{
- CLog::Log(LOGDEBUG, "GLX: Destroying glPixmap");
- glXDestroyPixmap(m_Display, m_glPixmap);
- m_glPixmap = None;
+ if(!vdp->ConfigVDPAU(s, max_refs))
+ return;
+ }
+
+ uint64_t startTime = CurrentHostCounter();
+ uint16_t decoded, processed, rend;
+ vdp->m_bufferStats.Get(decoded, processed, rend);
+ vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_decoder_render(vdp->m_vdpauConfig.vdpDecoder,
+ surf,
+ (VdpPictureInfo const *)&(vdp->m_hwContext.info),
+ vdp->m_hwContext.bitstream_buffers_used,
+ vdp->m_hwContext.bitstream_buffers);
+ if (vdp->CheckStatus(vdp_st, __LINE__))
+ vdp->m_DecoderError = true;
+ else
+ vdp->m_DecoderError = false;
+
+ uint64_t diff = CurrentHostCounter() - startTime;
+ if (diff*1000/CurrentHostFrequency() > 30)
+ CLog::Log(LOGDEBUG, "CVDPAU::DrawSlice - VdpDecoderRender long decoding: %d ms, dec: %d, proc: %d, rend: %d", (int)((diff*1000)/CurrentHostFrequency()), decoded, processed, rend);
+}
+
+
+int CDecoder::Decode(AVCodecContext *avctx, AVFrame *pFrame)
+{
+ int result = Check(avctx);
+ if (result)
+ return result;
+
+ CSingleLock lock(m_DecoderSection);
+
+ if (m_DecoderError && pFrame)
+ return VC_ERROR;
+
+ if (!m_vdpauConfigured)
+ return VC_ERROR;
+
+ if(pFrame)
+ { // we have a new frame from decoder
+
+ VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pFrame->data[3];
+ // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
+ if (!m_videoSurfaces.IsValid(surf))
+ {
+ CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
+ return VC_BUFFER;
+ }
+ m_videoSurfaces.MarkRender(surf);
+
+ // send frame to output for processing
+ CVdpauDecodedPicture pic;
+ memset(&pic.DVDPic, 0, sizeof(pic.DVDPic));
+ ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&pic.DVDPic);
+ pic.videoSurface = surf;
+ pic.DVDPic.color_matrix = avctx->colorspace;
+ m_bufferStats.IncDecoded();
+ m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, &pic, sizeof(pic));
+
+ //TODO
+ // m_codecControl = pic.DVDPic.iFlags & (DVP_FLAG_DRAIN | DVP_FLAG_NO_POSTPROC);
}
- if (m_Pixmap)
+ int retval = 0;
+ uint16_t decoded, processed, render;
+ Message *msg;
+ while (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
{
- CLog::Log(LOGDEBUG, "GLX: Destroying XPixmap");
- XFreePixmap(m_Display, m_Pixmap);
- m_Pixmap = None;
+ if (msg->signal == COutputControlProtocol::ERROR)
+ {
+ m_DisplayState = VDPAU_ERROR;
+ retval |= VC_ERROR;
+ }
+ msg->Release();
}
- outputSurface = presentSurface = VDP_INVALID_HANDLE;
+ m_bufferStats.Get(decoded, processed, render);
- for (int i = 0; i < totalAvailableOutputSurfaces; i++)
+ uint64_t startTime = CurrentHostCounter();
+ while (!retval)
{
- if (outputSurfaces[i] == VDP_INVALID_HANDLE)
- continue;
- vdp_st = vdp_output_surface_destroy(outputSurfaces[i]);
- outputSurfaces[i] = VDP_INVALID_HANDLE;
- CheckStatus(vdp_st, __LINE__);
- }
+ // first fill the buffers to keep vdpau busy
+ // mixer will run with decoded >= 2. output is limited by number of output surfaces
+ // In case mixer is bypassed we limit by looking at processed
+ if (decoded < 3 && processed < 3)
+ {
+ retval |= VC_BUFFER;
+ }
+ else if (m_vdpauOutput.m_dataPort.ReceiveInMessage(&msg))
+ {
+ if (msg->signal == COutputDataProtocol::PICTURE)
+ {
+ 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_DisplayState = VDPAU_ERROR;
+ retval |= VC_ERROR;
+ }
+ msg->Release();
+ }
- if (videoMixer != VDP_INVALID_HANDLE)
+ if (decoded < 3 && processed < 3)
+ {
+ retval |= VC_BUFFER;
+ }
+
+ if (!retval && !m_inMsgEvent.WaitMSec(2000))
+ break;
+ }
+ uint64_t diff = CurrentHostCounter() - startTime;
+ if (retval & VC_PICTURE)
{
- vdp_st = vdp_video_mixer_destroy(videoMixer);
- videoMixer = VDP_INVALID_HANDLE;
- CheckStatus(vdp_st, __LINE__);
+ m_bufferStats.SetParams(diff, m_codecControl);
}
+ if (diff*1000/CurrentHostFrequency() > 50)
+ CLog::Log(LOGDEBUG,"CVDPAU::Decode long wait: %d", (int)((diff*1000)/CurrentHostFrequency()));
- if (m_BlackBar)
+ if (!retval)
{
- delete [] m_BlackBar;
- m_BlackBar = NULL;
+ CLog::Log(LOGERROR, "VDPAU::%s - timed out waiting for output message", __FUNCTION__);
+ m_DisplayState = VDPAU_ERROR;
+ retval |= VC_ERROR;
}
- while (!m_DVDVideoPics.empty())
- m_DVDVideoPics.pop();
+ return retval;
+}
+
+bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
+{
+ CSingleLock lock(m_DecoderSection);
+
+ if (m_DisplayState != VDPAU_OPEN)
+ return false;
+
+ *picture = m_presentPicture->DVDPic;
+ picture->vdpau = m_presentPicture;
return true;
}
-void CVDPAU::SpewHardwareAvailable() //Copyright (c) 2008 Wladimir J. van der Laan -- VDPInfo
+void CDecoder::Reset()
{
- VdpStatus rv;
- CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:");
- CLog::Log(LOGNOTICE,"name level macbs width height");
- CLog::Log(LOGNOTICE,"------------------------------------");
- for(unsigned int x=0; x<decoder_profile_count; ++x)
+ CSingleLock lock(m_DecoderSection);
+
+ if (!m_vdpauConfigured)
+ return;
+
+ Message *reply;
+ if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
+ &reply,
+ 2000))
{
- 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,
- &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
- if(rv == VDP_STATUS_OK && is_supported)
+ bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
{
- CLog::Log(LOGNOTICE,"%-16s %2i %5i %5i %5i\n", decoder_profiles[x].name,
- max_level, max_macroblocks, max_width, max_height);
+ CLog::Log(LOGERROR, "VDPAU::%s - flush returned error", __FUNCTION__);
+ m_DisplayState = VDPAU_ERROR;
}
+ else
+ m_bufferStats.Reset();
}
- CLog::Log(LOGNOTICE,"------------------------------------");
- m_feature_count = 0;
-#define CHECK_SUPPORT(feature) \
- do { \
- VdpBool supported; \
- if(vdp_video_mixer_query_feature_support(vdp_device, feature, &supported) == VDP_STATUS_OK && supported) { \
- CLog::Log(LOGNOTICE, "Mixer feature: "#feature); \
- m_features[m_feature_count++] = feature; \
- } \
- } while(false)
+ else
+ {
+ CLog::Log(LOGERROR, "VDPAU::%s - flush timed out", __FUNCTION__);
+ m_DisplayState = VDPAU_ERROR;
+ }
+}
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
-#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
- CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
-#endif
-#undef CHECK_SUPPORT
+bool CDecoder::CanSkipDeint()
+{
+ return m_bufferStats.CanSkipDeint();
+}
+void CDecoder::ReturnRenderPicture(CVdpauRenderPicture *renderPic)
+{
+ m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
}
-bool CVDPAU::IsSurfaceValid(vdpau_render_state *render)
+bool CDecoder::CheckStatus(VdpStatus vdp_st, int line)
{
- // find render state in queue
- bool found(false);
- unsigned int i;
- for(i = 0; i < m_videoSurfaces.size(); ++i)
+ if (vdp_st != VDP_STATUS_OK)
{
- if(m_videoSurfaces[i] == render)
+ CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+
+ m_ErrorCount++;
+
+ if(m_DisplayState == VDPAU_OPEN)
{
- found = true;
- break;
+ if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
+ {
+ m_DisplayEvent.Reset();
+ m_DisplayState = VDPAU_LOST;
+ }
+ else if (m_ErrorCount > 2)
+ m_DisplayState = VDPAU_ERROR;
}
+
+ return true;
}
- if (!found)
- {
- CLog::Log(LOGERROR,"%s - video surface not found", __FUNCTION__);
- return false;
+ m_ErrorCount = 0;
+ 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 (m_videoSurfaces[i]->surface == VDP_INVALID_HANDLE)
+ if (vdpau)
+ vdpau->ReturnRenderPicture(this);
+}
+
+void CVdpauRenderPicture::Sync()
+{
+#ifdef GL_ARB_sync
+ CSingleLock lock(renderPicSection);
+ if (usefence)
{
- m_videoSurfaces[i]->state = 0;
- return false;
+ if(glIsSync(fence))
+ {
+ glDeleteSync(fence);
+ fence = None;
+ }
+ fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
+#endif
+}
- return true;
+//-----------------------------------------------------------------------------
+// Mixer
+//-----------------------------------------------------------------------------
+CMixer::CMixer(CEvent *inMsgEvent) :
+ CThread("Vdpau Mixer"),
+ m_controlPort("ControlPort", inMsgEvent, &m_outMsgEvent),
+ m_dataPort("DataPort", inMsgEvent, &m_outMsgEvent)
+{
+ m_inMsgEvent = inMsgEvent;
}
-int CVDPAU::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic)
+CMixer::~CMixer()
{
- //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
- CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
- CVDPAU* vdp = (CVDPAU*)ctx->GetHardware();
- struct pictureAge* pA = &vdp->picAge;
+ Dispose();
+}
- // while we are waiting to recover we can't do anything
- CSharedLock lock(vdp->m_DecoderSection);
+void CMixer::Start()
+{
+ Create();
+}
+
+void CMixer::Dispose()
+{
+ m_bStop = true;
+ m_outMsgEvent.Set();
+ StopThread();
+
+ m_controlPort.Purge();
+ m_dataPort.Purge();
+}
+
+bool CMixer::IsActive()
+{
+ return IsRunning();
+}
+
+void CMixer::OnStartup()
+{
+ CLog::Log(LOGNOTICE, "CMixer::OnStartup: Output Thread created");
+}
+
+void CMixer::OnExit()
+{
+ CLog::Log(LOGNOTICE, "CMixer::OnExit: Output Thread terminated");
+}
- { CSharedLock dLock(vdp->m_DisplaySection);
- if(vdp->m_DisplayState != VDPAU_OPEN)
+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
+ {
+ 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
+ {
+ 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 = NULL;
+ Protocol *port = NULL;
+ 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.context->GetProcs().vdp_video_mixer_create(m_config.context->GetDevice(),
+ m_config.context->GetFeatureCount(),
+ m_config.context->GetFeatures(),
+ ARSIZE(parameters),
+ parameters,
+ parameter_values,
+ &m_videoMixer);
+ CheckStatus(vdp_st, __LINE__);
+
+}
+
+void CMixer::InitCSCMatrix(int Width)
+{
+ 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;
+}
+
+void CMixer::CheckFeatures()
+{
+ if (m_Upscale != m_config.upscale)
+ {
+ SetHWUpscaling();
+ m_Upscale = m_config.upscale;
+ }
+ if (m_Brightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness ||
+ m_Contrast != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast ||
+ m_ColorMatrix != m_mixerInput[1].DVDPic.color_matrix)
+ {
+ SetColor();
+ m_Brightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
+ m_Contrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
+ m_ColorMatrix = m_mixerInput[1].DVDPic.color_matrix;
+ }
+ if (m_NoiseReduction != CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
+ {
+ m_NoiseReduction = CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction;
+ SetNoiseReduction();
+ }
+ if (m_Sharpness != CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
+ {
+ m_Sharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
+ SetSharpness();
+ }
+ if (m_DeintMode != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode ||
+ m_Deint != CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod)
+ {
+ m_DeintMode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+ m_Deint = CMediaSettings::Get().GetCurrentVideoSettings().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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 != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)
+ m_Procamp.brightness = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness)-50) / 100;
+ if (m_Contrast != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)
+ m_Procamp.contrast = (float)((CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast)+50) / 100;
+
+ VdpColorStandard colorStandard;
+ switch(m_mixerInput[1].DVDPic.color_matrix)
+ {
+ case AVCOL_SPC_BT709:
+ colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
+ break;
+ case AVCOL_SPC_BT470BG:
+ case AVCOL_SPC_SMPTE170M:
+ colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
+ break;
+ case AVCOL_SPC_SMPTE240M:
+ colorStandard = VDP_COLOR_STANDARD_SMPTE_240M;
+ break;
+ case AVCOL_SPC_FCC:
+ case AVCOL_SPC_UNSPECIFIED:
+ case AVCOL_SPC_RGB:
+ default:
+ if(m_config.surfaceWidth > 1000)
+ colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
+ else
+ colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
+ }
+
+ VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
+ if (CSettings::Get().GetBool("videoscreen.limitedrange"))
+ {
+ float studioCSC[3][4];
+ GenerateStudioCSCMatrix(colorStandard, studioCSC);
+ void const * pm_CSCMatix[] = { &studioCSC };
+ vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix);
+ }
+ else
+ {
+ vdp_st = m_config.context->GetProcs().vdp_generate_csc_matrix(&m_Procamp, colorStandard, &m_CSCMatrix);
+ void const * pm_CSCMatix[] = { &m_CSCMatrix };
+ vdp_st = m_config.context->GetProcs().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 (!CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
+ {
+ VdpBool enabled[]= {0};
+ vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+ CheckStatus(vdp_st, __LINE__);
+ return;
+ }
+ VdpBool enabled[]={1};
+ vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+ CheckStatus(vdp_st, __LINE__);
+ void* nr[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction };
+ CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction);
+ vdp_st = m_config.context->GetProcs().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 (!CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
+ {
+ VdpBool enabled[]={0};
+ vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+ CheckStatus(vdp_st, __LINE__);
+ return;
+ }
+ VdpBool enabled[]={1};
+ vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+ CheckStatus(vdp_st, __LINE__);
+ void* sh[] = { &CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness };
+ CLog::Log(LOGNOTICE,"Setting Sharpness to %f",CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness);
+ vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, sh);
+ CheckStatus(vdp_st, __LINE__);
+}
+
+EINTERLACEMETHOD CMixer::GetDeinterlacingMethod(bool log /* = false */)
+{
+ EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().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 = CMediaSettings::Get().GetCurrentVideoSettings().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+ }
+ else
+ {
+ VdpBool enabled[]={0,0,0};
+ vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
+ }
+ }
+ CheckStatus(vdp_st, __LINE__);
+
+ SetDeintSkipChroma();
+
+ m_config.useInteropYuv = !CSettings::Get().GetBool("videoplayer.usevdpaumixer");
+}
+
+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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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.context->GetProcs().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_Upscale = 0;
+ m_SeenInterlaceFlag = false;
+ m_ColorMatrix = 0;
+ m_PostProc = false;
+ m_vdpError = false;
+
+ m_config.upscale = g_advancedSettings.m_videoVDPAUScaling;
+ m_config.useInteropYuv = !CSettings::Get().GetBool("videoplayer.usevdpaumixer");
+
+ CreateVdpauMixer();
+}
+
+void CMixer::Uninit()
+{
+ Flush();
+ while (!m_outputSurfaces.empty())
+ {
+ m_outputSurfaces.pop();
+ }
+ m_config.context->GetProcs().vdp_video_mixer_destroy(m_videoMixer);
+}
+
+void CMixer::Flush()
+{
+ while (!m_mixerInput.empty())
+ {
+ CVdpauDecodedPicture pic = m_mixerInput.back();
+ m_mixerInput.pop_back();
+ m_config.videoSurfaces->ClearRender(pic.videoSurface);
+ }
+ while (!m_decodedPics.empty())
+ {
+ CVdpauDecodedPicture pic = m_decodedPics.front();
+ m_decodedPics.pop();
+ m_config.videoSurfaces->ClearRender(pic.videoSurface);
+ }
+ Message *msg;
+ while (m_dataPort.ReceiveOutMessage(&msg))
+ {
+ if (msg->signal == CMixerDataProtocol::FRAME)
+ {
+ CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
+ m_config.videoSurfaces->ClearRender(pic.videoSurface);
+ }
+ else if (msg->signal == CMixerDataProtocol::BUFFER)
+ {
+ VdpOutputSurface *surf;
+ surf = (VdpOutputSurface*)msg->data;
+ m_outputSurfaces.push(*surf);
+ }
+ msg->Release();
+ }
+}
+
+void CMixer::InitCycle()
+{
+ CheckFeatures();
+ int flags;
+ uint64_t latency;
+ m_config.stats->GetParams(latency, flags);
+ // TODO
+ if (0) //flags & DVP_FLAG_NO_POSTPROC)
+ SetPostProcFeatures(false);
+ else
+ SetPostProcFeatures(true);
+
+ m_config.stats->SetCanSkipDeint(false);
+
+ EDEINTERLACEMODE mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+ EINTERLACEMETHOD method = GetDeinterlacingMethod();
+ bool interlaced = m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_INTERLACED;
+ m_SeenInterlaceFlag |= interlaced;
+
+ // TODO
+ if (//!(flags & DVP_FLAG_NO_POSTPROC) &&
+ (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);
+ }
+
+ // TODO
+ if (0) //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_mixersteps = 1;
+ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+ m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420;
+ m_config.useInteropYuv = true;
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "CMixer::%s - interlace method: %d not supported, setting to AUTO", __FUNCTION__, method);
+ 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);
+
+ CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod = VS_INTERLACEMETHOD_AUTO;
+ }
+ }
+ 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;
+
+ m_processPicture.crop = false;
+ 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;
+ if (m_SeenInterlaceFlag)
+ {
+ double ratio = (double)m_mixerInput[1].DVDPic.iDisplayHeight / m_mixerInput[1].DVDPic.iHeight;
+ m_mixerInput[1].DVDPic.iDisplayHeight = lrint(ratio*(m_mixerInput[1].DVDPic.iHeight-NUM_CROP_PIX*2));
+ m_processPicture.crop = true;
+ }
+ }
+ 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.videoSurface = m_mixerInput[1].videoSurface;
+}
+
+void CMixer::FiniCycle()
+{
+ // Keep video surfaces for one 2 cycles longer than used
+ // by mixer. This avoids blocking in decoder.
+ // NVidia recommends num_ref + 5
+ while (m_mixerInput.size() > 5)
+ {
+ CVdpauDecodedPicture &tmp = m_mixerInput.back();
+ if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
+ {
+ m_config.videoSurfaces->ClearRender(tmp.videoSurface);
+ }
+ m_mixerInput.pop_back();
+ }
+}
+
+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].videoSurface;
+ if (m_mixerInput.size() > 2)
+ past_surfaces[0] = m_mixerInput[2].videoSurface;
+ futu_surfaces[0] = m_mixerInput[0].videoSurface;
+ pastCount = 2;
+ futuCount = 1;
+ }
+ else
+ {
+ if(m_mixerstep == 0)
+ { // first field
+ if (m_mixerInput.size() > 3)
+ {
+ past_surfaces[3] = m_mixerInput[3].videoSurface;
+ past_surfaces[2] = m_mixerInput[3].videoSurface;
+ }
+ if (m_mixerInput.size() > 2)
+ {
+ past_surfaces[1] = m_mixerInput[2].videoSurface;
+ past_surfaces[0] = m_mixerInput[2].videoSurface;
+ }
+ futu_surfaces[0] = m_mixerInput[1].videoSurface;
+ futu_surfaces[1] = m_mixerInput[0].videoSurface;
+ }
+ else
+ { // second field
+ if (m_mixerInput.size() > 3)
+ {
+ past_surfaces[3] = m_mixerInput[3].videoSurface;
+ }
+ if (m_mixerInput.size() > 2)
+ {
+ past_surfaces[2] = m_mixerInput[2].videoSurface;
+ past_surfaces[1] = m_mixerInput[2].videoSurface;
+ }
+ past_surfaces[0] = m_mixerInput[1].videoSurface;
+ futu_surfaces[0] = m_mixerInput[0].videoSurface;
+ futu_surfaces[1] = m_mixerInput[0].videoSurface;
+
+ if (m_mixerInput[0].DVDPic.pts != DVD_NOPTS_VALUE &&
+ m_mixerInput[1].DVDPic.pts != DVD_NOPTS_VALUE)
+ {
+ m_processPicture.DVDPic.pts = m_mixerInput[1].DVDPic.pts +
+ (m_mixerInput[0].DVDPic.pts -
+ m_mixerInput[1].DVDPic.pts) / 2;
+ }
+ else
+ 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.context->GetProcs().vdp_video_mixer_render(m_videoMixer,
+ VDP_INVALID_HANDLE,
+ 0,
+ m_mixerfield,
+ pastCount,
+ past_surfaces,
+ m_mixerInput[1].videoSurface,
+ futuCount,
+ futu_surfaces,
+ &sourceRect,
+ m_processPicture.outputSurface,
+ &destRect,
+ &destRect,
+ 0,
+ NULL);
+ 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.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+ m_vdpError = true;
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Buffer Pool
+//-----------------------------------------------------------------------------
+
+VdpauBufferPool::VdpauBufferPool()
+{
+ CVdpauRenderPicture *pic;
+ for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
+ {
+ pic = new CVdpauRenderPicture(renderPicSec);
+ allRenderPics.push_back(pic);
+ }
+}
+
+VdpauBufferPool::~VdpauBufferPool()
+{
+ CVdpauRenderPicture *pic;
+ for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
+ {
+ pic = allRenderPics[i];
+ delete pic;
+ }
+ allRenderPics.clear();
+}
+
+//-----------------------------------------------------------------------------
+// Output
+//-----------------------------------------------------------------------------
+COutput::COutput(CEvent *inMsgEvent) :
+ CThread("Vdpau Output"),
+ m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
+ m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
+ m_mixer(&m_outMsgEvent)
+{
+ m_inMsgEvent = inMsgEvent;
+
+ for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); ++i)
+ {
+ m_bufferPool.freeRenderPics.push_back(i);
+ }
+}
+
+void COutput::Start()
+{
+ Create();
+}
+
+COutput::~COutput()
+{
+ Dispose();
+
+ m_bufferPool.freeRenderPics.clear();
+ m_bufferPool.usedRenderPics.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);
+ QueueReturnPicture(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, &m_config, sizeof(m_config));
+ }
+ 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();
+ PreCleanup();
+ 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);
+ QueueReturnPicture(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:
+ if (ProcessSyncPicture())
+ m_extTimeout = 10;
+ else
+ m_extTimeout = 100;
+ if (HasWork())
+ {
+ m_state = O_TOP_CONFIGURED_WORK;
+ m_extTimeout = 0;
+ }
+ 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 = 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 = NULL;
+ 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;
+ }
+ // 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();
+ glFlush();
+ while(ProcessSyncPicture())
+ {
+ Sleep(10);
+ }
+ GLUnmapSurfaces();
+ ReleaseBufferPool();
+ DestroyGlxContext();
+ return true;
+}
+
+void COutput::Flush()
+{
+ if (m_mixer.IsActive())
+ {
+ 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;
+ m_bufferPool.processedPics.push(pic);
+ }
+ msg->Release();
+ }
+
+ while (m_dataPort.ReceiveOutMessage(&msg))
+ {
+ if (msg->signal == COutputDataProtocol::NEWFRAME)
+ {
+ CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data;
+ m_config.videoSurfaces->ClearRender(pic.videoSurface);
+ }
+ else if (msg->signal == COutputDataProtocol::RETURNPIC)
+ {
+ CVdpauRenderPicture *pic;
+ pic = *((CVdpauRenderPicture**)msg->data);
+ QueueReturnPicture(pic);
+ }
+ msg->Release();
+ }
+
+ while (m_dataPort.ReceiveInMessage(&msg))
+ {
+ if (msg->signal == COutputDataProtocol::PICTURE)
+ {
+ CVdpauRenderPicture *pic;
+ pic = *((CVdpauRenderPicture**)msg->data);
+ QueueReturnPicture(pic);
+ }
+ }
+
+ // reset used render flag which was cleared on mixer flush
+ std::deque<int>::iterator it;
+ for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
+ {
+ CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[*it];
+ if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
+ {
+ std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it2;
+ it2 = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
+ if (it2 == m_bufferPool.glVideoSurfaceMap.end())
+ {
+ CLog::Log(LOGDEBUG, "COutput::Flush - gl surface not found");
+ continue;
+ }
+ m_config.videoSurfaces->MarkRender(it2->second.sourceVuv);
+ }
+ }
+
+ // clear processed pics
+ while(!m_bufferPool.processedPics.empty())
+ {
+ CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front();
+ if (procPic.DVDPic.format == RENDER_FMT_VDPAU)
+ {
+ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &procPic.outputSurface, sizeof(procPic.outputSurface));
+ }
+ else if (procPic.DVDPic.format == RENDER_FMT_VDPAU_420)
+ {
+ m_config.videoSurfaces->ClearRender(procPic.videoSurface);
+ }
+ m_bufferPool.processedPics.pop();
+ }
+}
+
+bool COutput::HasWork()
+{
+ if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
+ return true;
+ return false;
+}
+
+CVdpauRenderPicture* COutput::ProcessMixerPicture()
+{
+ CVdpauRenderPicture *retPic = NULL;
+
+ if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty())
+ {
+ int idx = m_bufferPool.freeRenderPics.front();
+ retPic = m_bufferPool.allRenderPics[idx];
+ m_bufferPool.freeRenderPics.pop_front();
+ m_bufferPool.usedRenderPics.push_back(idx);
+ 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();
+ GLMapSurface(false, procPic.outputSurface);
+ retPic->sourceIdx = procPic.outputSurface;
+ retPic->texture[0] = m_bufferPool.glOutputSurfaceMap[procPic.outputSurface].texture[0];
+ retPic->texWidth = m_config.outWidth;
+ retPic->texHeight = m_config.outHeight;
+ retPic->crop.x1 = 0;
+ retPic->crop.y1 = procPic.crop ? NUM_CROP_PIX : 0;
+ retPic->crop.x2 = m_config.outWidth;
+ retPic->crop.y2 = m_config.outHeight - retPic->crop.y1;
+ }
+ else
+ {
+ m_config.useInteropYuv = true;
+ GLMapSurface(true, procPic.videoSurface);
+ retPic->sourceIdx = procPic.videoSurface;
+ for (unsigned int i=0; i<4; ++i)
+ retPic->texture[i] = m_bufferPool.glVideoSurfaceMap[procPic.videoSurface].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::QueueReturnPicture(CVdpauRenderPicture *pic)
+{
+ std::deque<int>::iterator it;
+ for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
+ {
+ if (m_bufferPool.allRenderPics[*it] == pic)
+ {
+ break;
+ }
+ }
+
+ if (it == m_bufferPool.usedRenderPics.end())
+ {
+ CLog::Log(LOGWARNING, "COutput::QueueReturnPicture - pic not found");
+ return;
+ }
+
+ // check if already queued
+ std::deque<int>::iterator it2 = find(m_bufferPool.syncRenderPics.begin(),
+ m_bufferPool.syncRenderPics.end(),
+ *it);
+ if (it2 == m_bufferPool.syncRenderPics.end())
+ {
+ m_bufferPool.syncRenderPics.push_back(*it);
+ }
+
+ ProcessSyncPicture();
+}
+
+bool COutput::ProcessSyncPicture()
+{
+ CVdpauRenderPicture *pic;
+ bool busy = false;
+
+ std::deque<int>::iterator it;
+ for (it = m_bufferPool.syncRenderPics.begin(); it != m_bufferPool.syncRenderPics.end(); )
+ {
+ pic = m_bufferPool.allRenderPics[*it];
+
+#ifdef GL_ARB_sync
+ if (pic->usefence)
+ {
+ if (glIsSync(pic->fence))
+ {
+ GLint state;
+ GLsizei length;
+ glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
+ if(state == GL_SIGNALED)
+ {
+ glDeleteSync(pic->fence);
+ pic->fence = None;
+ }
+ else
+ {
+ busy = true;
+ ++it;
+ continue;
+ }
+ }
+ }
+#endif
+
+ m_bufferPool.freeRenderPics.push_back(*it);
+
+ std::deque<int>::iterator it2 = find(m_bufferPool.usedRenderPics.begin(),
+ m_bufferPool.usedRenderPics.end(),
+ *it);
+ if (it2 == m_bufferPool.usedRenderPics.end())
+ {
+ CLog::Log(LOGERROR, "COutput::ProcessSyncPicture - pic not found in queue");
+ }
+ else
{
- CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
- return -1;
+ m_bufferPool.usedRenderPics.erase(it2);
}
- }
+ it = m_bufferPool.syncRenderPics.erase(it);
- 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)))
+ if (pic->valid)
{
- render = vdp->m_videoSurfaces[i];
- render->state = 0;
- break;
+ ProcessReturnPicture(pic);
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "COutput::%s - return of invalid render pic", __FUNCTION__);
}
}
+ return busy;
+}
- VdpStatus vdp_st = VDP_STATUS_ERROR;
- if (render == NULL)
+void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic)
+{
+ if (pic->DVDPic.format == RENDER_FMT_VDPAU_420)
{
- // create a new surface
- VdpDecoderProfile profile;
- ReadFormatOf(avctx->codec_id, profile, vdp->vdp_chroma_type);
- render = (vdpau_render_state*)calloc(sizeof(vdpau_render_state), 1);
- if (render == NULL)
+ std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+ it = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx);
+ if (it == m_bufferPool.glVideoSurfaceMap.end())
{
- CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - calloc failed");
- return -1;
+ CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
+ return;
}
- render->surface = VDP_INVALID_HANDLE;
- vdp->m_videoSurfaces.push_back(render);
+#ifdef GL_NV_vdpau_interop
+ glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
+#endif
+ VdpVideoSurface surf = it->second.sourceVuv;
+ m_config.videoSurfaces->ClearRender(surf);
}
-
- if (render->surface == VDP_INVALID_HANDLE)
+ else if (pic->DVDPic.format == RENDER_FMT_VDPAU)
{
- vdp_st = vdp->vdp_video_surface_create(vdp->vdp_device,
- vdp->vdp_chroma_type,
- avctx->coded_width,
- avctx->coded_height,
- &render->surface);
- vdp->CheckStatus(vdp_st, __LINE__);
- if (vdp_st != VDP_STATUS_OK)
+ std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+ it = m_bufferPool.glOutputSurfaceMap.find(pic->sourceIdx);
+ if (it == m_bufferPool.glOutputSurfaceMap.end())
{
- free(render);
- CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
- return -1;
+ CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found");
+ return;
}
+#ifdef GL_NV_vdpau_interop
+ glVDPAUUnmapSurfacesNV(1, &(it->second.glVdpauSurface));
+#endif
+ VdpOutputSurface outSurf = it->second.sourceRgb;
+ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf));
}
+}
- pic->data[1] = pic->data[2] = NULL;
- pic->data[0] = (uint8_t*)render;
- pic->data[3] = (uint8_t*)(uintptr_t)render->surface;
+bool COutput::EnsureBufferPool()
+{
+ VdpStatus vdp_st;
- pic->linesize[0] = pic->linesize[1] = pic->linesize[2] = 0;
+ // Creation of outputSurfaces
+ VdpOutputSurface outputSurface;
+ for (int i = m_bufferPool.outputSurfaces.size(); i < m_bufferPool.numOutputSurfaces; i++)
+ {
+ vdp_st = m_config.context->GetProcs().vdp_output_surface_create(m_config.context->GetDevice(),
+ VDP_RGBA_FORMAT_B8G8R8A8,
+ m_config.outWidth,
+ m_config.outHeight,
+ &outputSurface);
+ if (CheckStatus(vdp_st, __LINE__))
+ return false;
+ m_bufferPool.outputSurfaces.push_back(outputSurface);
- if(pic->reference)
- {
- pA->ip_age[0]= pA->ip_age[1]+1;
- pA->ip_age[1]= 1;
- pA->b_age++;
+ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
+ &outputSurface,
+ sizeof(VdpOutputSurface));
+ CLog::Log(LOGNOTICE, "VDPAU::COutput::InitBufferPool - Output Surface created");
}
- 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;
- pic->reordered_opaque= avctx->reordered_opaque;
- return 0;
+ return true;
}
-void CVDPAU::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic)
+void COutput::ReleaseBufferPool()
{
- //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
- CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
- CVDPAU* vdp = (CVDPAU*)ctx->GetHardware();
- vdpau_render_state * render;
- unsigned int i;
+ VdpStatus vdp_st;
- CSharedLock lock(vdp->m_DecoderSection);
+ CSingleLock lock(m_bufferPool.renderPicSec);
- render=(vdpau_render_state*)pic->data[0];
- if(!render)
+ // release all output surfaces
+ for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
{
- CLog::Log(LOGERROR, "CVDPAU::FFReleaseBuffer - invalid context handle provided");
- return;
+ if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
+ continue;
+ vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
+ CheckStatus(vdp_st, __LINE__);
}
+ m_bufferPool.outputSurfaces.clear();
- for(i=0; i<4; i++)
- pic->data[i]= NULL;
-
- // find render state in queue
- if (!vdp->IsSurfaceValid(render))
+ // wait for all fences
+ XbmcThreads::EndTime timeout(1000);
+ for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
{
- CLog::Log(LOGDEBUG, "CVDPAU::FFReleaseBuffer - ignoring invalid buffer");
- return;
+ CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[i];
+ if (pic->usefence)
+ {
+#ifdef GL_ARB_sync
+ while (glIsSync(pic->fence))
+ {
+ GLint state;
+ GLsizei length;
+ glGetSynciv(pic->fence, GL_SYNC_STATUS, 1, &length, &state);
+ if(state == GL_SIGNALED || timeout.IsTimePast())
+ {
+ glDeleteSync(pic->fence);
+ }
+ else
+ {
+ Sleep(5);
+ }
+ }
+ pic->fence = None;
+#endif
+ }
}
+ if (timeout.IsTimePast())
+ {
+ CLog::Log(LOGERROR, "COutput::%s - timeout waiting for fence", __FUNCTION__);
+ }
+ ProcessSyncPicture();
- render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE;
-}
-
-VdpStatus CVDPAU::Render(VdpDecoder decoder, VdpVideoSurface target,
- VdpPictureInfo const *picture_info,
- uint32_t bitstream_buffer_count,
- VdpBitstreamBuffer const * bitstream_buffers)
-{
- return VDP_STATUS_OK;
+ // invalidate all used render pictures
+ for (unsigned int i = 0; i < m_bufferPool.usedRenderPics.size(); ++i)
+ {
+ CVdpauRenderPicture *pic = m_bufferPool.allRenderPics[m_bufferPool.usedRenderPics[i]];
+ pic->valid = false;
+ }
}
-void CVDPAU::FFDrawSlice(struct AVCodecContext *s,
- const AVFrame *src, int offset[4],
- int y, int type, int height)
+void COutput::PreCleanup()
{
- CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)s->opaque;
- CVDPAU* vdp = (CVDPAU*)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;
- }
+ VdpStatus vdp_st;
+ m_mixer.Dispose();
+ ProcessSyncPicture();
- if(src->linesize[0] || src->linesize[1] || src->linesize[2]
- || offset[0] || offset[1] || offset[2])
+ CSingleLock lock(m_bufferPool.renderPicSec);
+ for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
{
- CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
- return;
- }
+ if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE)
+ continue;
- VdpStatus vdp_st;
- vdpau_render_state * render;
+ // check if output surface is in use
+ bool used = false;
+ std::deque<int>::iterator it;
+ CVdpauRenderPicture *pic;
+ for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it)
+ {
+ pic = m_bufferPool.allRenderPics[*it];
+ if ((pic->sourceIdx == m_bufferPool.outputSurfaces[i]) && pic->valid)
+ {
+ used = true;
+ break;
+ }
+ }
+ if (used)
+ continue;
+
+#ifdef GL_NV_vdpau_interop
+ // unmap surface
+ std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it_map;
+ it_map = m_bufferPool.glOutputSurfaceMap.find(m_bufferPool.outputSurfaces[i]);
+ if (it_map == m_bufferPool.glOutputSurfaceMap.end())
+ {
+ CLog::Log(LOGERROR, "%s - could not find gl surface", __FUNCTION__);
+ continue;
+ }
+ glVDPAUUnregisterSurfaceNV(it_map->second.glVdpauSurface);
+ glDeleteTextures(1, it_map->second.texture);
+ m_bufferPool.glOutputSurfaceMap.erase(it_map);
+#endif
- render = (vdpau_render_state*)src->data[0];
- if(!render)
- {
- CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid context handle provided");
- return;
- }
+ vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]);
+ CheckStatus(vdp_st, __LINE__);
- // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
- if (!vdp->IsSurfaceValid(render))
- {
- CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
- return;
+ m_bufferPool.outputSurfaces[i] = VDP_INVALID_HANDLE;
+
+ CLog::Log(LOGDEBUG, "VDPAU::PreCleanup - released output surface");
}
- uint32_t max_refs = 0;
- if(s->codec_id == AV_CODEC_ID_H264)
- max_refs = vdp->m_hwContext.info.h264.num_ref_frames;
+}
- if(vdp->decoder == VDP_INVALID_HANDLE
- || vdp->vdpauConfigured == false
- || vdp->max_references < max_refs)
+void COutput::InitMixer()
+{
+ for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i)
{
- if(!vdp->ConfigVDPAU(s, max_refs))
- return;
+ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
+ &m_bufferPool.outputSurfaces[i],
+ sizeof(VdpOutputSurface));
}
-
- vdp_st = vdp->vdp_decoder_render(vdp->decoder,
- render->surface,
- (VdpPictureInfo const *)&(vdp->m_hwContext.info),
- vdp->m_hwContext.bitstream_buffers_used,
- vdp->m_hwContext.bitstream_buffers);
- vdp->CheckStatus(vdp_st, __LINE__);
}
-int CVDPAU::Decode(AVCodecContext *avctx, AVFrame *pFrame)
+bool COutput::GLInit()
{
- //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
- VdpStatus vdp_st;
- VdpTime time;
-
- int result = Check(avctx);
- if (result)
- return result;
-
- CSharedLock lock(m_DecoderSection);
-
- if (!vdpauConfigured)
- return VC_ERROR;
-
- // configure vdpau output
- if (!ConfigOutputMethod(avctx, pFrame))
- return VC_FLUSHED;
-
- outputSurface = outputSurfaces[surfaceNum];
+#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
- CheckFeatures();
+#ifdef 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");
+
+ while (glGetError() != GL_NO_ERROR);
+ glVDPAUInitNV(reinterpret_cast<void*>(m_config.context->GetDevice()), reinterpret_cast<void*>(m_config.context->GetProcs().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
- if (( (int)outRectVid.x1 != OutWidth ) ||
- ( (int)outRectVid.y1 != OutHeight ))
+#ifdef GL_ARB_sync
+ bool hasfence = glewIsSupported("GL_ARB_sync");
+ for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); i++)
{
- outRectVid.x0 = 0;
- outRectVid.y0 = 0;
- outRectVid.x1 = OutWidth;
- outRectVid.y1 = OutHeight;
+ m_bufferPool.allRenderPics[i]->usefence = hasfence;
}
+#endif
- EDEINTERLACEMODE mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
- EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
- if (method == VS_INTERLACEMETHOD_AUTO)
- method = AutoInterlaceMethod();
-
- if(pFrame)
- { // we have a new frame from decoder
+ return true;
+}
- vdpau_render_state * render = (vdpau_render_state*)pFrame->data[2];
- if(!render) // old style ffmpeg gave data on plane 0
- render = (vdpau_render_state*)pFrame->data[0];
- if(!render)
- return VC_ERROR;
+void COutput::GLMapSurface(bool yuv, uint32_t source)
+{
+#ifdef GL_NV_vdpau_interop
- // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
- if (!IsSurfaceValid(render))
+ if (yuv)
+ {
+ std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+ it = m_bufferPool.glVideoSurfaceMap.find(source);
+ if (it == m_bufferPool.glVideoSurfaceMap.end())
{
- CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
- return VC_BUFFER;
- }
+ VdpauBufferPool::GLVideoSurface glSurface;
+ VdpVideoSurface surf = source;
+
+ if (surf == VDP_INVALID_HANDLE)
+ return;
- render->state |= FF_VDPAU_STATE_USED_FOR_RENDER;
+ glSurface.sourceVuv = surf;
+ 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<void*>(surf),
+ GL_TEXTURE_2D, 4, glSurface.texture);
- ClearUsedForRender(&past[0]);
- past[0] = past[1];
- past[1] = current;
- current = future;
- future = render;
+ 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;
+ }
+ m_bufferPool.glVideoSurfaceMap[surf] = glSurface;
- DVDVideoPicture DVDPic;
- memset(&DVDPic, 0, sizeof(DVDVideoPicture));
- ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&DVDPic);
- m_DVDVideoPics.push(DVDPic);
+ CLog::Log(LOGNOTICE, "VDPAU::COutput registered surface");
+ }
- int pics = m_DVDVideoPics.size();
- if (pics < 2)
- return VC_BUFFER;
- else if (pics > 2)
+ while (glGetError() != GL_NO_ERROR) ;
+ glVDPAUMapSurfacesNV(1, &m_bufferPool.glVideoSurfaceMap[source].glVdpauSurface);
+ if (glGetError() != GL_NO_ERROR)
{
- // this should not normally happen
- CLog::Log(LOGERROR, "CVDPAU::Decode - invalid number of pictures in queue");
- while (pics-- != 2)
- m_DVDVideoPics.pop();
+ CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
+ m_vdpError = true;
}
- if (mode == VS_DEINTERLACEMODE_FORCE
- || (mode == VS_DEINTERLACEMODE_AUTO && m_DVDVideoPics.front().iFlags & DVP_FLAG_INTERLACED))
+ if (m_vdpError)
+ return;
+ }
+ else
+ {
+ std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+ it = m_bufferPool.glOutputSurfaceMap.find(source);
+ if (it == m_bufferPool.glOutputSurfaceMap.end())
{
- 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 ))
+ unsigned int idx = 0;
+ for (idx = 0; idx<m_bufferPool.outputSurfaces.size(); idx++)
{
- 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_bufferPool.outputSurfaces[idx] == source)
+ break;
+ }
- 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;
+ VdpauBufferPool::GLVideoSurface glSurface;
+ glSurface.sourceRgb = m_bufferPool.outputSurfaces[idx];
+ glGenTextures(1, glSurface.texture);
+ glSurface.glVdpauSurface = glVDPAURegisterOutputSurfaceNV(reinterpret_cast<void*>(m_bufferPool.outputSurfaces[idx]),
+ GL_TEXTURE_2D, 1, glSurface.texture);
+ if (glGetError() != GL_NO_ERROR)
+ {
+ CLog::Log(LOGERROR, "VDPAU::COutput error register output surface");
+ m_vdpError = true;
}
- else
+ glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY);
+ if (glGetError() != GL_NO_ERROR)
{
- m_mixerstep = 0;
- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+ CLog::Log(LOGERROR, "VDPAU::COutput error setting access");
+ m_vdpError = true;
}
+ m_bufferPool.glOutputSurfaceMap[source] = glSurface;
+ CLog::Log(LOGNOTICE, "VDPAU::COutput registered output surfaces");
}
- else
+
+ while (glGetError() != GL_NO_ERROR) ;
+ glVDPAUMapSurfacesNV(1, &m_bufferPool.glOutputSurfaceMap[source].glVdpauSurface);
+ if (glGetError() != GL_NO_ERROR)
{
- m_mixerstep = 0;
- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+ CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface");
+ m_vdpError = true;
}
+ if (m_vdpError)
+ return;
}
- else if(m_mixerstep == 1)
- { // no new frame given, output second field of old frame
+#endif
+}
- if(avctx->skip_frame == AVDISCARD_NONREF)
- {
- ClearUsedForRender(&past[1]);
- m_DVDVideoPics.pop();
- return VC_BUFFER;
- }
+void COutput::GLUnmapSurfaces()
+{
+#ifdef GL_NV_vdpau_interop
- 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;
- }
- else
{
- CLog::Log(LOGERROR, "CVDPAU::Decode - invalid mixer state reached");
- return VC_BUFFER;
+ std::map<VdpVideoSurface, VdpauBufferPool::GLVideoSurface>::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();
}
- 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)
+ std::map<VdpOutputSurface, VdpauBufferPool::GLVideoSurface>::iterator it;
+ for (it = m_bufferPool.glOutputSurfaceMap.begin(); it != m_bufferPool.glOutputSurfaceMap.end(); ++it)
{
- if (past[0])
- past_surfaces[1] = past[0]->surface;
- if (past[1])
- past_surfaces[0] = past[1]->surface;
- futu_surfaces[0] = future->surface;
+ glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface);
+ glDeleteTextures(1, it->second.texture);
}
- else
- {
- 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;
- }
- }
-
- 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__);
+ m_bufferPool.glOutputSurfaceMap.clear();
- surfaceNum++;
- if (surfaceNum >= totalAvailableOutputSurfaces) surfaceNum = 0;
+ glVDPAUFiniNV();
- 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
-
- 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__);
+ CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop finished");
- clipRect = outRectVid;
- clipRect.y0 = clipRect.y1 - 2;
- vdp_st = vdp_output_surface_put_bits_native(outputSurface,
- (void**)data,
- pitches,
- &clipRect);
- CheckStatus(vdp_st, __LINE__);
+#endif
+}
- if(m_mixerstep == 1)
- return VC_PICTURE;
- else
- {
- ClearUsedForRender(&past[1]);
- return VC_BUFFER | VC_PICTURE;
- }
+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.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+ m_vdpError = true;
+ return true;
}
+ return false;
}
-bool CVDPAU::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
+bool COutput::CreateGlxContext()
{
- CSharedLock lock(m_DecoderSection);
-
- { CSharedLock dLock(m_DisplaySection);
- if (m_DisplayState != VDPAU_OPEN)
- return false;
- }
+ GLXContext glContext;
- *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();
+ m_Display = g_Windowing.GetDisplay();
+ glContext = g_Windowing.GetGlxContext();
+ m_Window = g_Windowing.GetWindow();
- picture->format = RENDER_FMT_VDPAU;
- picture->iFlags &= DVP_FLAG_DROPPED;
- picture->iWidth = OutWidth;
- picture->iHeight = OutHeight;
- picture->vdpau = this;
+ // Get our window attribs.
+ XWindowAttributes wndattribs;
+ XGetWindowAttributes(m_Display, m_Window, &wndattribs);
- if(m_mixerstep)
+ // Get visual Info
+ XVisualInfo visInfo;
+ visInfo.visualid = wndattribs.visual->visualid;
+ int nvisuals = 0;
+ XVisualInfo* visuals = XGetVisualInfo(m_Display, VisualIDMask, &visInfo, &nvisuals);
+ if (nvisuals != 1)
{
- picture->iRepeatPicture = -0.5;
- if(m_mixerstep > 1)
- {
- picture->dts = DVD_NOPTS_VALUE;
- picture->pts = DVD_NOPTS_VALUE;
- }
+ CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - could not find visual");
+ return false;
}
- return true;
-}
+ visInfo = visuals[0];
+ XFree(visuals);
-void CVDPAU::Reset()
-{
- // invalidate surfaces and picture queue when seeking
- ClearUsedForRender(&past[0]);
- ClearUsedForRender(&past[1]);
- ClearUsedForRender(¤t);
- ClearUsedForRender(&future);
+ 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;
+ }
- while (!m_DVDVideoPics.empty())
- m_DVDVideoPics.pop();
-}
+ // create gl pixmap
+ m_glPixmap = glXCreateGLXPixmap(m_Display, &visInfo, m_pixmap);
-void CVDPAU::Present()
-{
- //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
- VdpStatus vdp_st;
+ if (!m_glPixmap)
+ {
+ CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not create glPixmap");
+ return false;
+ }
- CSharedLock lock(m_DecoderSection);
+ m_glContext = glXCreateContext(m_Display, &visInfo, glContext, True);
- { CSharedLock dLock(m_DisplaySection);
- if (m_DisplayState != VDPAU_OPEN)
- return;
+ if (!glXMakeCurrent(m_Display, m_glPixmap, m_glContext))
+ {
+ CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not make Pixmap current");
+ return false;
}
- presentSurface = outputSurface;
-
- vdp_st = vdp_presentation_queue_display(vdp_flip_queue,
- presentSurface,
- 0,
- 0,
- 0);
- CheckStatus(vdp_st, __LINE__);
+ CLog::Log(LOGNOTICE, "VDPAU::COutput::CreateGlxContext - created context");
+ return true;
}
-bool CVDPAU::CheckStatus(VdpStatus vdp_st, int line)
+bool COutput::DestroyGlxContext()
{
- if (vdp_st != VDP_STATUS_OK)
+ if (m_glContext)
{
- CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
+ glXMakeCurrent(m_Display, None, NULL);
+ glXDestroyContext(m_Display, m_glContext);
+ }
+ m_glContext = 0;
- CExclusiveLock lock(m_DisplaySection);
+ if (m_glPixmap)
+ glXDestroyPixmap(m_Display, m_glPixmap);
+ m_glPixmap = 0;
- if(m_DisplayState == VDPAU_OPEN)
- {
- if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
- {
- m_DisplayEvent.Reset();
- m_DisplayState = VDPAU_LOST;
- }
- else
- m_DisplayState = VDPAU_ERROR;
- }
+ if (m_pixmap)
+ XFreePixmap(m_Display, m_pixmap);
+ m_pixmap = 0;
- return true;
- }
- return false;
+ return true;
}
#endif