Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

xvba: add decoder

  • Loading branch information...
commit d1b59e7d1773d734e728c4b1205f6d604b938825 1 parent 09b2b7b
@FernetMenta authored
View
48 configure.in
@@ -124,6 +124,8 @@ vaapi_not_found="== Could not find libva. VAAPI support disabled. =="
vaapi_disabled="== VAAPI support manually disabled. =="
crystalhd_not_found="== Could not find libcrystalhd. CrystalHD support disabled. =="
crystalhd_disabled="== CrystalHD support manually disabled. =="
+xvba_not_found="== Could not find amdxvba.h. XVBA support disabled. =="
+xvba_disabled="== XVBA support manually disabled. =="
vdadecoder_enabled="== VDADecoder support enabled. =="
vdadecoder_disabled="== VDADecoder support manually disabled. =="
vtbdecoder_enabled="== VTBDecoder support enabled. =="
@@ -247,6 +249,12 @@ AC_ARG_ENABLE([crystalhd],
[enable CrystalHD decoding (default is auto)])],
[use_crystalhd=$enableval],
[use_crystalhd=auto])
+
+AC_ARG_ENABLE([xvba],
+ [AS_HELP_STRING([--enable-xvba],
+ [enable XVBA decoding (default is auto)])],
+ [use_xvba=$enableval],
+ [use_xvba=auto])
AC_ARG_ENABLE([vdadecoder],
[AS_HELP_STRING([--enable-vdadecoder],
@@ -1759,6 +1767,38 @@ else
USE_CRYSTALHD=0
fi
+# XVBA
+if test "x$use_xvba" != "xno"; then
+ if test "$host_vendor" = "apple" ; then
+ if test "x$use_xvba" = "xyes"; then
+ AC_MSG_ERROR([XVBA not supported on this platform])
+ else
+ use_xvba="no"
+ AC_MSG_NOTICE($xvba_disabled)
+ fi
+ USE_XVBA=0
+ else
+ initial_val=$use_xvba
+ AC_CHECK_HEADER([amd/amdxvba.h],, use_xvba=no, [#include <X11/Xlib.h>])
+
+ if test "x$use_xvba" = "xno"; then
+ if test "x$initial_val" = "xyes"; then
+ AC_MSG_ERROR($xvba_not_found)
+ else
+ AC_MSG_RESULT($xvba_not_found)
+ fi
+ USE_XVBA=0
+ else
+ AC_DEFINE([HAVE_LIBXVBA], [1], [Define to 1 if you have the 'xvba' header (amdxvba.h)])
+ USE_XVBA=1
+ fi
+ fi
+else
+ AC_MSG_NOTICE($xvba_disabled)
+ USE_XVBA=0
+fi
+
+
# VDADecoder
if test "x$use_vdadecoder" != "xno"; then
if test "$host_vendor" = "apple" ; then
@@ -1970,6 +2010,12 @@ else
final_message="$final_message\n CrystalHD:\tNo"
fi
+if test "x$use_xvba" != "xno"; then
+ final_message="$final_message\n XVBA:\t\tYes"
+else
+ final_message="$final_message\n XVBA:\t\tNo"
+fi
+
if test "x$use_vdadecoder" != "xno"; then
final_message="$final_message\n VDADecoder:\tYes"
else
@@ -2443,6 +2489,7 @@ AC_SUBST(USE_OPENGLES)
AC_SUBST(USE_VDPAU)
AC_SUBST(USE_VAAPI)
AC_SUBST(USE_CRYSTALHD)
+AC_SUBST(USE_XVBA)
AC_SUBST(USE_LIBSMBCLIENT)
AC_SUBST(USE_LIBNFS)
AC_SUBST(USE_LIBAFPCLIENT)
@@ -2626,6 +2673,7 @@ XB_CONFIG_MODULE([lib/ffmpeg], [
`if test "x$use_vdpau" != "xno"; then echo --enable-vdpau; else echo --disable-vdpau; fi` \
`if test "x$use_vaapi" != "xno"; then echo --enable-vaapi; else echo --disable-vaapi; fi` \
`if test "$use_optimizations" != "no"; then echo --enable-optimizations; else echo --disable-optimizations; fi` \
+ `if test "x$use_xvba" != "xno"; then echo --enable-xvba; else echo --disable-xvba; fi` \
--enable-protocol=http \
--enable-pthreads \
--enable-runtime-cpudetect \
View
12 language/English/strings.po
@@ -5124,7 +5124,11 @@ msgctxt "#13436"
msgid "Allow Vdpau OpenGL interop YUV"
msgstr ""
-#empty strings from id 13437 to 13499
+msgctxt "#13437"
+msgid "Allow hardware acceleration (XVBA)"
+msgstr ""
+
+#empty strings from id 13438 to 13499
msgctxt "#13500"
msgid "A/V sync method"
@@ -6346,7 +6350,11 @@ msgctxt "#16325"
msgid "VDPAU - Bob"
msgstr ""
-#empty strings from id 16326 to 16399
+msgctxt "#16326"
+msgid "XVBA"
+msgstr ""
+
+#empty strings from id 16327 to 16399
msgctxt "#16400"
msgid "Post-processing"
View
218 xbmc/cores/VideoRenderers/LinuxRendererGL.cpp
@@ -63,6 +63,9 @@
VA_MICRO_VERSION == 0 && VA_SDS_VERSION < 5)))
#endif
+#ifdef HAVE_LIBXVBA
+#include "cores/dvdplayer/DVDCodecs/Video/XVBA.h"
+#endif
#ifdef TARGET_DARWIN
#include "osx/CocoaInterface.h"
@@ -129,6 +132,9 @@ CLinuxRendererGL::YUVBUFFER::YUVBUFFER()
#ifdef HAVE_LIBVDPAU
vdpau = NULL;
#endif
+#ifdef HAVE_LIBXVBA
+ xvba = NULL;
+#endif
}
CLinuxRendererGL::YUVBUFFER::~YUVBUFFER()
@@ -604,6 +610,9 @@ void CLinuxRendererGL::ReleaseBuffer(int idx)
#ifdef HAVE_LIBVDPAU
SAFE_RELEASE(buf.vdpau);
#endif
+#ifdef HAVE_LIBXVBA
+ SAFE_RELEASE(buf.xvba);
+#endif
#ifdef HAVE_LIBVA
buf.vaapi.surface.reset();
#endif
@@ -879,7 +888,7 @@ void CLinuxRendererGL::UpdateVideoFilter()
case VS_SCALINGMETHOD_LINEAR:
SetTextureFilter(m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR);
m_renderQuality = RQ_SINGLEPASS;
- if (((m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI)) && m_nonLinStretch)
+ if (((m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI) || (m_renderMethod & RENDER_XVBA)) && m_nonLinStretch)
{
m_pVideoFilterShader = new StretchFilterShader();
if (!m_pVideoFilterShader->CompileAndLink())
@@ -965,6 +974,11 @@ void CLinuxRendererGL::LoadShaders(int field)
CLog::Log(LOGNOTICE, "GL: Using CVBREF render method");
m_renderMethod = RENDER_CVREF;
}
+ else if (m_format == RENDER_FMT_XVBA)
+ {
+ CLog::Log(LOGNOTICE, "GL: Using XVBA render method");
+ m_renderMethod = RENDER_XVBA;
+ }
else
{
int requestedMethod = g_guiSettings.GetInt("videoplayer.rendermethod");
@@ -1113,6 +1127,12 @@ void CLinuxRendererGL::LoadShaders(int field)
m_textureCreate = &CLinuxRendererGL::CreateCVRefTexture;
m_textureDelete = &CLinuxRendererGL::DeleteCVRefTexture;
}
+ else if (m_format == RENDER_FMT_XVBA)
+ {
+ m_textureUpload = &CLinuxRendererGL::UploadXVBATexture;
+ m_textureCreate = &CLinuxRendererGL::CreateXVBATexture;
+ m_textureDelete = &CLinuxRendererGL::DeleteXVBATexture;
+ }
else
{
// setup default YV12 texture handlers
@@ -1225,6 +1245,13 @@ void CLinuxRendererGL::Render(DWORD flags, int renderBuffer)
RenderVAAPI(renderBuffer, m_currentField);
}
#endif
+#ifdef HAVE_LIBXVBA
+ else if (m_renderMethod & RENDER_XVBA)
+ {
+ UpdateVideoFilter();
+ RenderXVBA(renderBuffer, m_currentField);
+ }
+#endif
else
{
// RENDER_CVREF uses the same render as the default case
@@ -1732,6 +1759,77 @@ void CLinuxRendererGL::RenderVAAPI(int index, int field)
#endif
}
+void CLinuxRendererGL::RenderXVBA(int index, int field)
+{
+#ifdef HAVE_LIBXVBA
+ YUVPLANE &plane = m_buffers[index].fields[0][1];
+
+ glEnable(m_textureTarget);
+ glActiveTextureARB(GL_TEXTURE0);
+
+ glBindTexture(m_textureTarget, plane.id);
+
+ // Try some clamping or wrapping
+ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (m_pVideoFilterShader)
+ {
+ GLint filter;
+ if (!m_pVideoFilterShader->GetTextureFilter(filter))
+ filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR;
+
+ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, filter);
+ m_pVideoFilterShader->SetSourceTexture(0);
+ m_pVideoFilterShader->SetWidth(m_sourceWidth);
+ m_pVideoFilterShader->SetHeight(m_sourceHeight);
+
+ //disable non-linear stretch when a dvd menu is shown, parts of the menu are rendered through the overlay renderer
+ //having non-linear stretch on breaks the alignment
+ if (g_application.m_pPlayer && g_application.m_pPlayer->IsInMenu())
+ m_pVideoFilterShader->SetNonLinStretch(1.0);
+ else
+ m_pVideoFilterShader->SetNonLinStretch(pow(g_settings.m_fPixelRatio, g_advancedSettings.m_videoNonLinStretchRatio));
+
+ m_pVideoFilterShader->Enable();
+ }
+ else
+ {
+ GLint filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR;
+ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, filter);
+ }
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ VerifyGLState();
+
+ glBegin(GL_QUADS);
+ if (m_textureTarget==GL_TEXTURE_2D)
+ {
+ glTexCoord2f(plane.rect.x1, plane.rect.y1); glVertex2f(m_destRect.x1, m_destRect.y1);
+ glTexCoord2f(plane.rect.x2, plane.rect.y1); glVertex2f(m_destRect.x2, m_destRect.y1);
+ glTexCoord2f(plane.rect.x2, plane.rect.y2); glVertex2f(m_destRect.x2, m_destRect.y2);
+ glTexCoord2f(plane.rect.x1, plane.rect.y2); glVertex2f(m_destRect.x1, m_destRect.y2);
+ }
+ else
+ {
+ glTexCoord2f(m_destRect.x1, m_destRect.y1); glVertex4f(m_destRect.x1, m_destRect.y1, 0.0f, 0.0f);
+ glTexCoord2f(m_destRect.x2, m_destRect.y1); glVertex4f(m_destRect.x2, m_destRect.y1, 1.0f, 0.0f);
+ glTexCoord2f(m_destRect.x2, m_destRect.y2); glVertex4f(m_destRect.x2, m_destRect.y2, 1.0f, 1.0f);
+ glTexCoord2f(m_destRect.x1, m_destRect.y2); glVertex4f(m_destRect.x1, m_destRect.y2, 0.0f, 1.0f);
+ }
+ glEnd();
+ VerifyGLState();
+
+ if (m_pVideoFilterShader)
+ m_pVideoFilterShader->Disable();
+
+ glBindTexture (m_textureTarget, 0);
+ glDisable(m_textureTarget);
+#endif
+}
+
void CLinuxRendererGL::RenderSoftware(int index, int field)
{
// used for textues uploaded from rgba or CVPixelBuffers.
@@ -2783,6 +2881,93 @@ bool CLinuxRendererGL::CreateCVRefTexture(int index)
return true;
}
+void CLinuxRendererGL::DeleteXVBATexture(int index)
+{
+#ifdef HAVE_LIBXVBA
+ YUVPLANE &plane = m_buffers[index].fields[0][0];
+ YUVFIELDS &fields = m_buffers[index].fields;
+
+ SAFE_RELEASE(m_buffers[index].xvba);
+
+ if(plane.id && glIsTexture(plane.id))
+ glDeleteTextures(1, &plane.id);
+ plane.id = 0;
+ fields[0][1].id = 0;
+#endif
+}
+
+bool CLinuxRendererGL::CreateXVBATexture(int index)
+{
+#ifdef HAVE_LIBXVBA
+ YV12Image &im = m_buffers[index].image;
+ YUVFIELDS &fields = m_buffers[index].fields;
+ YUVPLANE &plane = fields[0][0];
+
+ DeleteXVBATexture(index);
+
+ memset(&im , 0, sizeof(im));
+ memset(&fields, 0, sizeof(fields));
+
+ glGenTextures(1, &plane.id);
+
+ m_eventTexturesDone[index]->Set();
+#endif
+ return true;
+}
+
+void CLinuxRendererGL::UploadXVBATexture(int index)
+{
+#ifdef HAVE_LIBXVBA
+ XVBA::CXvbaRenderPicture *xvba = m_buffers[index].xvba;
+ YV12Image &im = m_buffers[index].image;
+
+ YUVFIELDS &fields = m_buffers[index].fields;
+ YUVPLANE &plane = fields[0][1];
+
+ if (!xvba)
+ {
+ fields[0][1].id = fields[0][0].id;
+ m_eventTexturesDone[index]->Set();
+ CLog::Log(LOGWARNING,"CLinuxRendererGL::UploadXVBATexture no xvba texture, index: %d", index);
+ return;
+ }
+// xvba->Transfer();
+
+ fields[0][1].id = xvba->texture;
+
+ im.height = xvba->texHeight;
+ im.width = xvba->texWidth;
+
+ plane.texwidth = xvba->texWidth;
+ plane.texheight = xvba->texHeight;
+ plane.pixpertex_x = 1;
+ plane.pixpertex_y = 1;
+
+ plane.rect = m_sourceRect;
+ plane.width = im.width;
+ plane.height = im.height;
+
+ plane.height /= plane.pixpertex_y;
+ plane.rect.y1 /= plane.pixpertex_y;
+ plane.rect.y2 /= plane.pixpertex_y;
+ plane.width /= plane.pixpertex_x;
+ plane.rect.x1 /= plane.pixpertex_x;
+ plane.rect.x2 /= plane.pixpertex_x;
+
+ if (m_textureTarget == GL_TEXTURE_2D)
+ {
+ plane.height /= plane.texheight;
+ plane.rect.y1 /= plane.texheight;
+ plane.rect.y2 /= plane.texheight;
+ plane.width /= plane.texwidth;
+ plane.rect.x1 /= plane.texwidth;
+ plane.rect.x2 /= plane.texwidth;
+ }
+
+ m_eventTexturesDone[index]->Set();
+#endif
+}
+
void CLinuxRendererGL::UploadYUV422PackedTexture(int source)
{
YUVBUFFER& buf = m_buffers[source];
@@ -3368,6 +3553,9 @@ bool CLinuxRendererGL::Supports(ERENDERFEATURE feature)
if (m_renderMethod & RENDER_VAAPI)
return false;
+ if (m_renderMethod & RENDER_XVBA)
+ return false;
+
return (m_renderMethod & RENDER_GLSL)
|| (m_renderMethod & RENDER_ARB)
|| ((m_renderMethod & RENDER_SW) && glewIsSupported("GL_ARB_imaging") == GL_TRUE);
@@ -3381,6 +3569,9 @@ bool CLinuxRendererGL::Supports(ERENDERFEATURE feature)
if (m_renderMethod & RENDER_VAAPI)
return false;
+ if (m_renderMethod & RENDER_XVBA)
+ return false;
+
return (m_renderMethod & RENDER_GLSL)
|| (m_renderMethod & RENDER_ARB)
|| ((m_renderMethod & RENDER_SW) && glewIsSupported("GL_ARB_imaging") == GL_TRUE);
@@ -3404,7 +3595,8 @@ bool CLinuxRendererGL::Supports(ERENDERFEATURE feature)
if (feature == RENDERFEATURE_NONLINSTRETCH)
{
if (((m_renderMethod & RENDER_GLSL) && !(m_renderMethod & RENDER_POT)) ||
- (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI))
+ (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI) ||
+ (m_renderMethod & RENDER_XVBA))
return true;
}
@@ -3476,6 +3668,16 @@ bool CLinuxRendererGL::Supports(EINTERLACEMETHOD method)
return false;
}
+ if(m_renderMethod & RENDER_XVBA)
+ {
+#ifdef HAVE_LIBXVBA
+ XVBA::CXvbaRenderPicture *xvba = m_buffers[m_iYV12RenderBuffer].xvba;
+ if(xvba)
+ return xvba->xvba->Supports(method);
+#endif
+ return false;
+ }
+
#ifdef TARGET_DARWIN
// YADIF too slow for HD but we have no methods to fall back
// to something that works so just turn it off.
@@ -3518,7 +3720,7 @@ bool CLinuxRendererGL::Supports(ESCALINGMETHOD method)
|| method == VS_SCALINGMETHOD_LANCZOS3)
{
if ((glewIsSupported("GL_EXT_framebuffer_object") && (m_renderMethod & RENDER_GLSL)) ||
- (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI))
+ (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI) || (m_renderMethod & RENDER_XVBA))
{
// spline36 and lanczos3 are only allowed through advancedsettings.xml
if(method != VS_SCALINGMETHOD_SPLINE36
@@ -3610,4 +3812,14 @@ void CLinuxRendererGL::AddProcessor(struct __CVBuffer *cvBufferRef, int index)
}
#endif
+#ifdef HAVE_LIBXVBA
+void CLinuxRendererGL::AddProcessor(XVBA::CXvbaRenderPicture* xvba, int index)
+{
+ YUVBUFFER &buf = m_buffers[index];
+ XVBA::CXvbaRenderPicture *pic = xvba->Acquire();
+ SAFE_RELEASE(buf.xvba);
+ buf.xvba = pic;
+}
+#endif
+
#endif
View
15 xbmc/cores/VideoRenderers/LinuxRendererGL.h
@@ -43,6 +43,8 @@ namespace Shaders { class BaseYUV2RGBShader; }
namespace Shaders { class BaseVideoFilterShader; }
namespace VAAPI { struct CHolder; }
namespace VDPAU { class CVdpauRenderPicture; }
+namespace XVBA { class CXvbaRenderPicture; }
+
#define NUM_BUFFERS 10
@@ -90,6 +92,7 @@ enum RenderMethod
RENDER_POT=0x10,
RENDER_VAAPI=0x20,
RENDER_CVREF = 0x40,
+ RENDER_XVBA=0x80,
};
enum RenderQuality
@@ -151,7 +154,9 @@ class CLinuxRendererGL : public CBaseRenderer
#ifdef TARGET_DARWIN
virtual void AddProcessor(struct __CVBuffer *cvBufferRef, int index);
#endif
-
+#ifdef HAVE_LIBXVBA
+ virtual void AddProcessor(XVBA::CXvbaRenderPicture* xvba, int index);
+#endif
virtual void RenderUpdate(bool clear, DWORD flags = 0, DWORD alpha = 255);
// Feature support
@@ -210,6 +215,10 @@ class CLinuxRendererGL : public CBaseRenderer
void DeleteYUV422PackedTexture(int index);
bool CreateYUV422PackedTexture(int index);
+ void UploadXVBATexture(int index);
+ void DeleteXVBATexture(int index);
+ bool CreateXVBATexture(int index);
+
void UploadRGBTexture(int index);
void ToRGBFrame(YV12Image* im, unsigned flipIndexPlane, unsigned flipIndexBuf);
void ToRGBFields(YV12Image* im, unsigned flipIndexPlaneTop, unsigned flipIndexPlaneBot, unsigned flipIndexBuf);
@@ -225,6 +234,7 @@ class CLinuxRendererGL : public CBaseRenderer
void RenderVDPAU(int renderBuffer, int field); // render using vdpau hardware
void RenderProgressiveWeave(int renderBuffer, int field); // render using vdpau hardware
void RenderVAAPI(int renderBuffer, int field); // render using vdpau hardware
+ void RenderXVBA(int renderBuffer, int field); // render using xvba hardware
struct
{
@@ -292,6 +302,9 @@ class CLinuxRendererGL : public CBaseRenderer
#ifdef TARGET_DARWIN_OSX
struct __CVBuffer *cvBufferRef;
#endif
+#ifdef HAVE_LIBXVBA
+ XVBA::CXvbaRenderPicture *xvba;
+#endif
};
typedef YUVBUFFER YUVBUFFERS[NUM_BUFFERS];
View
1  xbmc/cores/VideoRenderers/RenderFormats.h
@@ -35,6 +35,7 @@ enum ERenderFormat {
RENDER_FMT_OMXEGL,
RENDER_FMT_CVBREF,
RENDER_FMT_BYPASS,
+ RENDER_FMT_XVBA,
};
#endif
View
9 xbmc/cores/VideoRenderers/RenderManager.cpp
@@ -250,8 +250,9 @@ bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsi
// check if decoder supports buffering
m_bCodecSupportsBuffering = false;
- if (format == RENDER_FMT_VDPAU ||
- format == RENDER_FMT_VDPAU_420)
+ if (format == RENDER_FMT_VDPAU
+ || format == RENDER_FMT_VDPAU_420
+ || format == RENDER_FMT_XVBA)
m_bCodecSupportsBuffering = true;
bool result = m_pRenderer->Configure(width, height, d_width, d_height, fps, flags, format, extended_format, orientation);
@@ -873,6 +874,10 @@ int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
else if(pic.format == RENDER_FMT_VAAPI)
m_pRenderer->AddProcessor(*pic.vaapi, index);
#endif
+#ifdef HAVE_LIBXVBA
+ else if(pic.format == RENDER_FMT_XVBA)
+ m_pRenderer->AddProcessor(pic.xvba, index);
+#endif
m_pRenderer->ReleaseImage(index, false);
return index;
View
4 xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
@@ -35,6 +35,7 @@
namespace DXVA { class CSurfaceContext; }
namespace VAAPI { struct CHolder; }
namespace VDPAU { class CVdpauRenderPicture; }
+namespace XVBA { class CXvbaRenderPicture; }
class COpenMax;
class COpenMaxVideo;
struct OpenMaxVideoBuffer;
@@ -60,6 +61,9 @@ struct DVDVideoPicture
struct {
VAAPI::CHolder* vaapi;
};
+ struct {
+ XVBA::CXvbaRenderPicture* xvba;
+ };
struct {
COpenMax *openMax;
View
16 xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
@@ -56,6 +56,9 @@
#ifdef HAVE_LIBVA
#include "VAAPI.h"
#endif
+#ifdef HAVE_LIBXVBA
+#include "XVBA.h"
+#endif
using namespace boost;
@@ -100,6 +103,19 @@ enum PixelFormat CDVDVideoCodecFFmpeg::GetFormat( struct AVCodecContext * avctx
dec->Release();
}
#endif
+#ifdef HAVE_LIBXVBA
+ if(*cur == PIX_FMT_XVBA_VLD && g_guiSettings.GetBool("videoplayer.usexvba"))
+ {
+ XVBA::CDecoder* dec = new XVBA::CDecoder();
+ if(dec->Open(avctx, *cur, ctx->m_uSurfacesCount))
+ {
+ ctx->SetHardware(dec);
+ return *cur;
+ }
+ else
+ dec->Release();
+ }
+#endif
#ifdef HAVE_LIBVA
// mpeg4 vaapi decoding is disabled
if(*cur == PIX_FMT_VAAPI_VLD && g_guiSettings.GetBool("videoplayer.usevaapi")
View
4 xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
@@ -14,6 +14,10 @@ ifeq (@USE_CRYSTALHD@,1)
SRCS += CrystalHD.cpp
SRCS += DVDVideoCodecCrystalHD.cpp
endif
+ifeq (@USE_XVBA@,1)
+SRCS+= XVBA.cpp \
+
+endif
ifeq (@USE_VDA@,1)
SRCS += DVDVideoCodecVDA.cpp
endif
View
2,354 xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.cpp
@@ -0,0 +1,2354 @@
+/*
+ * Copyright (C) 2005-2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#ifdef HAVE_LIBXVBA
+
+#include "system_gl.h"
+#include <dlfcn.h>
+#include <string>
+#include "XVBA.h"
+#include "windowing/WindowingFactory.h"
+#include "guilib/GraphicContext.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "utils/TimeUtils.h"
+#include "cores/dvdplayer/DVDClock.h"
+
+using namespace XVBA;
+
+// XVBA interface
+
+#define XVBA_LIBRARY "libXvBAW.so.1"
+
+typedef Bool (*XVBAQueryExtensionProc) (Display *dpy, int *vers);
+typedef Status (*XVBACreateContextProc) (void *input, void *output);
+typedef Status (*XVBADestroyContextProc) (void *context);
+typedef Bool (*XVBAGetSessionInfoProc) (void *input, void *output);
+typedef Status (*XVBACreateSurfaceProc) (void *input, void *output);
+typedef Status (*XVBACreateGLSharedSurfaceProc)(void *input, void *output);
+typedef Status (*XVBADestroySurfaceProc) (void *surface);
+typedef Status (*XVBACreateDecodeBuffersProc) (void *input, void *output);
+typedef Status (*XVBADestroyDecodeBuffersProc) (void *input);
+typedef Status (*XVBAGetCapDecodeProc) (void *input, void *output);
+typedef Status (*XVBACreateDecodeProc) (void *input, void *output);
+typedef Status (*XVBADestroyDecodeProc) (void *session);
+typedef Status (*XVBAStartDecodePictureProc) (void *input);
+typedef Status (*XVBADecodePictureProc) (void *input);
+typedef Status (*XVBAEndDecodePictureProc) (void *input);
+typedef Status (*XVBASyncSurfaceProc) (void *input, void *output);
+typedef Status (*XVBAGetSurfaceProc) (void *input);
+typedef Status (*XVBATransferSurfaceProc) (void *input);
+
+static struct
+{
+ XVBAQueryExtensionProc QueryExtension;
+ XVBACreateContextProc CreateContext;
+ XVBADestroyContextProc DestroyContext;
+ XVBAGetSessionInfoProc GetSessionInfo;
+ XVBACreateSurfaceProc CreateSurface;
+ XVBACreateGLSharedSurfaceProc CreateGLSharedSurface;
+ XVBADestroySurfaceProc DestroySurface;
+ XVBACreateDecodeBuffersProc CreateDecodeBuffers;
+ XVBADestroyDecodeBuffersProc DestroyDecodeBuffers;
+ XVBAGetCapDecodeProc GetCapDecode;
+ XVBACreateDecodeProc CreateDecode;
+ XVBADestroyDecodeProc DestroyDecode;
+ XVBAStartDecodePictureProc StartDecodePicture;
+ XVBADecodePictureProc DecodePicture;
+ XVBAEndDecodePictureProc EndDecodePicture;
+ XVBASyncSurfaceProc SyncSurface;
+ XVBAGetSurfaceProc GetSurface;
+ XVBATransferSurfaceProc TransferSurface;
+}g_XVBA_vtable;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+CXVBAContext *CXVBAContext::m_context = 0;
+CCriticalSection CXVBAContext::m_section;
+Display *CXVBAContext::m_display = 0;
+void *CXVBAContext::m_dlHandle = 0;
+
+CXVBAContext::CXVBAContext()
+{
+ m_context = 0;
+// m_dlHandle = 0;
+ m_xvbaContext = 0;
+ m_refCount = 0;
+}
+
+void CXVBAContext::Release()
+{
+ CSingleLock lock(m_section);
+
+ m_refCount--;
+ if (m_refCount <= 0)
+ {
+ Close();
+ delete this;
+ m_context = 0;
+ }
+}
+
+void CXVBAContext::Close()
+{
+ CLog::Log(LOGNOTICE, "XVBA::Close - closing decoder context");
+
+ DestroyContext();
+// if (m_dlHandle)
+// {
+// dlclose(m_dlHandle);
+// m_dlHandle = 0;
+// }
+}
+
+bool CXVBAContext::EnsureContext(CXVBAContext **ctx)
+{
+ CSingleLock lock(m_section);
+
+ if (m_context)
+ {
+ m_context->m_refCount++;
+ *ctx = m_context;
+ return true;
+ }
+
+ m_context = new CXVBAContext();
+ *ctx = m_context;
+ {
+ CSingleLock gLock(g_graphicsContext);
+ if (!m_context->LoadSymbols() || !m_context->CreateContext())
+ {
+ delete m_context;
+ m_context = 0;
+ return false;
+ }
+ }
+
+ m_context->m_refCount++;
+
+ *ctx = m_context;
+ return true;
+}
+
+bool CXVBAContext::LoadSymbols()
+{
+ if (!m_dlHandle)
+ {
+ m_dlHandle = dlopen(XVBA_LIBRARY, RTLD_LAZY);
+ if (!m_dlHandle)
+ {
+ const char* error = dlerror();
+ if (!error)
+ error = "dlerror() returned NULL";
+
+ CLog::Log(LOGERROR,"XVBA::LoadSymbols: Unable to get handle to lib: %s", error);
+ return false;
+ }
+ }
+ else
+ return true;
+
+#define INIT_PROC(PREFIX, PROC) do { \
+ g_##PREFIX##_vtable.PROC = (PREFIX##PROC##Proc) \
+ dlsym(m_dlHandle, #PREFIX #PROC); \
+ } while (0)
+
+#define INIT_PROC_CHECK(PREFIX, PROC) do { \
+ dlerror(); \
+ INIT_PROC(PREFIX, PROC); \
+ if (dlerror()) { \
+ dlclose(m_dlHandle); \
+ m_dlHandle = NULL; \
+ return false; \
+ } \
+ } while (0)
+
+#define XVBA_INIT_PROC(PROC) INIT_PROC_CHECK(XVBA, PROC)
+
+ XVBA_INIT_PROC(QueryExtension);
+ XVBA_INIT_PROC(CreateContext);
+ XVBA_INIT_PROC(DestroyContext);
+ XVBA_INIT_PROC(GetSessionInfo);
+ XVBA_INIT_PROC(CreateSurface);
+ XVBA_INIT_PROC(CreateGLSharedSurface);
+ XVBA_INIT_PROC(DestroySurface);
+ XVBA_INIT_PROC(CreateDecodeBuffers);
+ XVBA_INIT_PROC(DestroyDecodeBuffers);
+ XVBA_INIT_PROC(GetCapDecode);
+ XVBA_INIT_PROC(CreateDecode);
+ XVBA_INIT_PROC(DestroyDecode);
+ XVBA_INIT_PROC(StartDecodePicture);
+ XVBA_INIT_PROC(DecodePicture);
+ XVBA_INIT_PROC(EndDecodePicture);
+ XVBA_INIT_PROC(SyncSurface);
+ XVBA_INIT_PROC(GetSurface);
+ XVBA_INIT_PROC(TransferSurface);
+
+#undef XVBA_INIT_PROC
+#undef INIT_PROC
+
+ return true;
+}
+
+bool CXVBAContext::CreateContext()
+{
+ if (m_xvbaContext)
+ return true;
+
+ CLog::Log(LOGNOTICE,"XVBA::CreateContext - creating decoder context");
+
+ Drawable window;
+ { CSingleLock lock(g_graphicsContext);
+ if (!m_display)
+ m_display = XOpenDisplay(NULL);
+ window = 0;
+ }
+
+ int version;
+ if (!g_XVBA_vtable.QueryExtension(m_display, &version))
+ return false;
+ CLog::Log(LOGNOTICE,"XVBA::CreateContext - opening xvba version: %i", version);
+
+ // create XVBA Context
+ XVBA_Create_Context_Input contextInput;
+ XVBA_Create_Context_Output contextOutput;
+ contextInput.size = sizeof(contextInput);
+ contextInput.display = m_display;
+ contextInput.draw = window;
+ contextOutput.size = sizeof(contextOutput);
+ if(Success != g_XVBA_vtable.CreateContext(&contextInput, &contextOutput))
+ {
+ CLog::Log(LOGERROR,"XVBA::CreateContext - failed to create context");
+ return false;
+ }
+ m_xvbaContext = contextOutput.context;
+
+ return true;
+}
+
+void CXVBAContext::DestroyContext()
+{
+ if (!m_xvbaContext)
+ return;
+
+ g_XVBA_vtable.DestroyContext(m_xvbaContext);
+ m_xvbaContext = 0;
+// XCloseDisplay(m_display);
+}
+
+void *CXVBAContext::GetContext()
+{
+ return m_xvbaContext;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+static unsigned int decoderId = 0;
+
+CDecoder::CDecoder() : m_xvbaOutput(&m_inMsgEvent)
+{
+ m_xvbaConfig.context = 0;
+ m_xvbaConfig.xvbaSession = 0;
+ m_xvbaConfig.videoSurfaces = &m_videoSurfaces;
+ m_xvbaConfig.videoSurfaceSec = &m_videoSurfaceSec;
+ m_xvbaConfig.apiSec = &m_apiSec;
+
+ m_displayState = XVBA_OPEN;
+}
+
+CDecoder::~CDecoder()
+{
+ Close();
+}
+
+typedef struct {
+ unsigned int size;
+ unsigned int num_of_decodecaps;
+ XVBADecodeCap decode_caps_list[];
+} XVBA_GetCapDecode_Output_Base;
+
+void CDecoder::OnLostDevice()
+{
+ CLog::Log(LOGNOTICE,"XVBA::OnLostDevice event");
+
+ CSingleLock lock(m_decoderSection);
+ DestroySession();
+ if (m_xvbaConfig.context)
+ m_xvbaConfig.context->Release();
+ m_xvbaConfig.context = 0;
+
+ m_displayState = XVBA_LOST;
+ m_displayEvent.Reset();
+}
+
+void CDecoder::OnResetDevice()
+{
+ CLog::Log(LOGNOTICE,"XVBA::OnResetDevice event");
+
+ CSingleLock lock(m_decoderSection);
+ if (m_displayState == XVBA_LOST)
+ {
+ m_displayState = XVBA_RESET;
+ lock.Leave();
+ m_displayEvent.Set();
+ }
+}
+
+bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat fmt, unsigned int surfaces)
+{
+ std::string Vendor = g_Windowing.GetRenderVendor();
+ std::transform(Vendor.begin(), Vendor.end(), Vendor.begin(), ::tolower);
+ if (Vendor.compare(0, 3, "ati") != 0)
+ {
+ return false;
+ }
+
+ m_decoderId = decoderId++;
+
+ CLog::Log(LOGNOTICE,"(XVBA::Open) opening xvba decoder, id: %d", m_decoderId);
+
+ if(avctx->coded_width == 0
+ || avctx->coded_height == 0)
+ {
+ CLog::Log(LOGWARNING,"(XVBA) no width/height available, can't init");
+ return false;
+ }
+
+ if (!m_dllAvUtil.Load())
+ return false;
+
+ if (!CXVBAContext::EnsureContext(&m_xvbaConfig.context))
+ return false;
+
+ // xvba get session info
+ XVBA_GetSessionInfo_Input sessionInput;
+ XVBA_GetSessionInfo_Output sessionOutput;
+ sessionInput.size = sizeof(sessionInput);
+ sessionInput.context = m_xvbaConfig.context->GetContext();
+ sessionOutput.size = sizeof(sessionOutput);
+ if (Success != g_XVBA_vtable.GetSessionInfo(&sessionInput, &sessionOutput))
+ {
+ CLog::Log(LOGERROR,"(XVBA) can't get session info");
+ return false;
+ }
+ if (sessionOutput.getcapdecode_output_size == 0)
+ {
+ CLog::Log(LOGERROR,"(XVBA) session decode not supported");
+ return false;
+ }
+
+ // get decoder capabilities
+ XVBA_GetCapDecode_Input capInput;
+ XVBA_GetCapDecode_Output *capOutput;
+ capInput.size = sizeof(capInput);
+ capInput.context = m_xvbaConfig.context->GetContext();
+ capOutput = (XVBA_GetCapDecode_Output *)calloc(sessionOutput.getcapdecode_output_size, 1);
+ capOutput->size = sessionOutput.getcapdecode_output_size;
+ if (Success != g_XVBA_vtable.GetCapDecode(&capInput, capOutput))
+ {
+ CLog::Log(LOGERROR,"(XVBA) can't get decode capabilities");
+ return false;
+ }
+
+ int match = -1;
+ if (avctx->codec_id == CODEC_ID_H264)
+ {
+ // search for profile high
+ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i)
+ {
+ if (capOutput->decode_caps_list[i].capability_id == XVBA_H264 &&
+ capOutput->decode_caps_list[i].flags == XVBA_H264_HIGH)
+ {
+ match = (int) i;
+ break;
+ }
+ }
+ if (match < 0)
+ {
+ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_H264_HIGH not found");
+ }
+ }
+ else if (avctx->codec_id == CODEC_ID_VC1)
+ {
+ // search for profile advanced
+ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i)
+ {
+ if (capOutput->decode_caps_list[i].capability_id == XVBA_VC1 &&
+ capOutput->decode_caps_list[i].flags == XVBA_VC1_ADVANCED)
+ {
+ match = (int) i;
+ break;
+ }
+ }
+ if (match < 0)
+ {
+ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_VC1_ADVANCED not found");
+ }
+ }
+ else if (avctx->codec_id == CODEC_ID_MPEG2VIDEO)
+ {
+ // search for profile high
+ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i)
+ {
+ if (capOutput->decode_caps_list[i].capability_id == XVBA_MPEG2_VLD)
+ {
+ // XXX: uncomment when implemented
+ // match = (int) i;
+ // break;
+ }
+ }
+ if (match < 0)
+ {
+ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_MPEG2_VLD not found");
+ }
+ }
+ else if (avctx->codec_id == CODEC_ID_WMV3)
+ {
+ // search for profile high
+ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i)
+ {
+ if (capOutput->decode_caps_list[i].capability_id == XVBA_VC1 &&
+ capOutput->decode_caps_list[i].flags == XVBA_VC1_MAIN)
+ {
+ match = (int) i;
+ break;
+ }
+ }
+ if (match < 0)
+ {
+ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_VC1_MAIN not found");
+ }
+ }
+
+ if (match < 0)
+ {
+ free(capOutput);
+ return false;
+ }
+
+ CLog::Log(LOGNOTICE,"(XVBA) using decoder capability id: %i flags: %i",
+ capOutput->decode_caps_list[match].capability_id,
+ capOutput->decode_caps_list[match].flags);
+ CLog::Log(LOGNOTICE,"(XVBA) using surface type: %x",
+ capOutput->decode_caps_list[match].surface_type);
+
+ m_xvbaConfig.decoderCap = capOutput->decode_caps_list[match];
+
+ free(capOutput);
+
+ // set some varables
+ m_xvbaConfig.xvbaSession = 0;
+ m_xvbaBufferPool.data_buffer = 0;
+ m_xvbaBufferPool.iq_matrix_buffer = 0;
+ m_xvbaBufferPool.picture_descriptor_buffer = 0;
+ m_presentPicture = 0;
+ m_xvbaConfig.numRenderBuffers = surfaces;
+ m_decoderThread = CThread::GetCurrentThreadId();
+ m_speed = DVD_PLAYSPEED_NORMAL;
+
+ if (1) //g_guiSettings.GetBool("videoplayer.usexvbasharedsurface"))
+ m_xvbaConfig.useSharedSurfaces = true;
+ else
+ m_xvbaConfig.useSharedSurfaces = false;
+
+ m_displayState = XVBA_OPEN;
+
+ // setup ffmpeg
+ avctx->thread_count = 1;
+ avctx->get_buffer = CDecoder::FFGetBuffer;
+ avctx->release_buffer = CDecoder::FFReleaseBuffer;
+ avctx->draw_horiz_band = CDecoder::FFDrawSlice;
+ avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
+
+ g_Windowing.Register(this);
+ return true;
+}
+
+void CDecoder::Close()
+{
+ CLog::Log(LOGNOTICE, "XVBA::Close - closing decoder, id: %d", m_decoderId);
+
+ if (!m_xvbaConfig.context)
+ return;
+
+ DestroySession();
+ if (m_xvbaConfig.context)
+ m_xvbaConfig.context->Release();
+ m_xvbaConfig.context = 0;
+
+ while (!m_videoSurfaces.empty())
+ {
+ xvba_render_state *render = m_videoSurfaces.back();
+ if(render->buffers_alllocated > 0)
+ m_dllAvUtil.av_free(render->buffers);
+ m_videoSurfaces.pop_back();
+ free(render);
+ }
+
+ g_Windowing.Unregister(this);
+ m_dllAvUtil.Unload();
+}
+
+long CDecoder::Release()
+{
+ // check if we should do some pre-cleanup here
+ // a second decoder might need resources
+ if (m_xvbaConfig.xvbaSession)
+ {
+ CSingleLock lock(m_decoderSection);
+ CLog::Log(LOGNOTICE,"XVBA::Release pre-cleanup");
+ DestroySession(true);
+ }
+ return IHardwareDecoder::Release();
+}
+
+long CDecoder::ReleasePicReference()
+{
+ return IHardwareDecoder::Release();
+}
+
+bool CDecoder::Supports(EINTERLACEMETHOD method)
+{
+ if(method == VS_INTERLACEMETHOD_AUTO)
+ return true;
+
+ if (1) //g_guiSettings.GetBool("videoplayer.usexvbasharedsurface"))
+ {
+ if (method == VS_INTERLACEMETHOD_XVBA)
+ return true;
+ }
+
+ return false;
+}
+
+void CDecoder::ResetState()
+{
+ m_displayState = XVBA_OPEN;
+}
+
+int CDecoder::Check(AVCodecContext* avctx)
+{
+ EDisplayState state;
+
+ { CSingleLock lock(m_decoderSection);
+ state = m_displayState;
+ }
+
+ if (state == XVBA_LOST)
+ {
+ CLog::Log(LOGNOTICE,"XVBA::Check waiting for display reset event");
+ if (!m_displayEvent.WaitMSec(2000))
+ {
+ CLog::Log(LOGERROR, "XVBA::Check - device didn't reset in reasonable time");
+ state = XVBA_RESET;;
+ }
+ else
+ { CSingleLock lock(m_decoderSection);
+ state = m_displayState;
+ }
+ }
+ if (state == XVBA_RESET || state == XVBA_ERROR)
+ {
+ CLog::Log(LOGNOTICE,"XVBA::Check - Attempting recovery");
+
+ CSingleLock gLock(g_graphicsContext);
+ CSingleLock lock(m_decoderSection);
+
+ DestroySession();
+ ResetState();
+ CXVBAContext::EnsureContext(&m_xvbaConfig.context);
+
+ if (state == XVBA_RESET)
+ return VC_FLUSHED;
+ else
+ return VC_ERROR;
+ }
+ return 0;
+}
+
+void CDecoder::SetError(const char* function, const char* msg, int line)
+{
+ CLog::Log(LOGERROR, "XVBA::%s - %s, line %d", function, msg, line);
+ CSingleLock lock(m_decoderSection);
+ m_displayState = XVBA_ERROR;
+}
+
+bool CDecoder::CreateSession(AVCodecContext* avctx)
+{
+ m_xvbaConfig.surfaceWidth = (avctx->coded_width+15) & ~15;
+ m_xvbaConfig.surfaceHeight = (avctx->coded_height+15) & ~15;
+
+ m_xvbaConfig.vidWidth = avctx->width;
+ m_xvbaConfig.vidHeight = avctx->height;
+
+ XVBA_Create_Decode_Session_Input sessionInput;
+ XVBA_Create_Decode_Session_Output sessionOutput;
+
+ sessionInput.size = sizeof(sessionInput);
+ sessionInput.width = m_xvbaConfig.surfaceWidth;
+ sessionInput.height = m_xvbaConfig.surfaceHeight;
+ sessionInput.context = m_xvbaConfig.context->GetContext();
+ sessionInput.decode_cap = &m_xvbaConfig.decoderCap;
+ sessionOutput.size = sizeof(sessionOutput);
+
+ if (Success != g_XVBA_vtable.CreateDecode(&sessionInput, &sessionOutput))
+ {
+ SetError(__FUNCTION__, "failed to create decoder session", __LINE__);
+ CLog::Log(LOGERROR, "Decoder failed with following stats: m_surfaceWidth %u, m_surfaceHeight %u,"
+ " m_vidWidth %u, m_vidHeight %u, coded_width %d, coded_height %d",
+ m_xvbaConfig.surfaceWidth,
+ m_xvbaConfig.surfaceHeight,
+ m_xvbaConfig.vidWidth,
+ m_xvbaConfig.vidHeight,
+ avctx->coded_width,
+ avctx->coded_height);
+ return false;
+ }
+ m_xvbaConfig.xvbaSession = sessionOutput.session;
+
+ // create decode buffers
+ XVBA_Create_DecodeBuff_Input bufferInput;
+ XVBA_Create_DecodeBuff_Output bufferOutput;
+
+ bufferInput.size = sizeof(bufferInput);
+ bufferInput.session = m_xvbaConfig.xvbaSession;
+ bufferInput.buffer_type = XVBA_PICTURE_DESCRIPTION_BUFFER;
+ bufferInput.num_of_buffers = 1;
+ bufferOutput.size = sizeof(bufferOutput);
+ if (Success != g_XVBA_vtable.CreateDecodeBuffers(&bufferInput, &bufferOutput)
+ || bufferOutput.num_of_buffers_in_list != 1)
+ {
+ SetError(__FUNCTION__, "failed to create picture buffer", __LINE__);
+ return false;
+ }
+ m_xvbaBufferPool.picture_descriptor_buffer = bufferOutput.buffer_list;
+
+ // data buffer
+ bufferInput.buffer_type = XVBA_DATA_BUFFER;
+ if (Success != g_XVBA_vtable.CreateDecodeBuffers(&bufferInput, &bufferOutput)
+ || bufferOutput.num_of_buffers_in_list != 1)
+ {
+ SetError(__FUNCTION__, "failed to create data buffer", __LINE__);
+ return false;
+ }
+ m_xvbaBufferPool.data_buffer = bufferOutput.buffer_list;
+
+ // QO Buffer
+ bufferInput.buffer_type = XVBA_QM_BUFFER;
+ if (Success != g_XVBA_vtable.CreateDecodeBuffers(&bufferInput, &bufferOutput)
+ || bufferOutput.num_of_buffers_in_list != 1)
+ {
+ SetError(__FUNCTION__, "failed to create qm buffer", __LINE__);
+ return false;
+ }
+ m_xvbaBufferPool.iq_matrix_buffer = bufferOutput.buffer_list;
+
+
+ // initialize output
+ CSingleLock lock(g_graphicsContext);
+ m_xvbaConfig.stats = &m_bufferStats;
+ m_bufferStats.Reset();
+ m_xvbaOutput.Start();
+ Message *reply;
+ if (m_xvbaOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT,
+ &reply,
+ 2000,
+ &m_xvbaConfig,
+ sizeof(m_xvbaConfig)))
+ {
+ bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "XVBA::%s - vdpau output returned error", __FUNCTION__);
+ m_xvbaOutput.Dispose();
+ return false;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "XVBA::%s - failed to init output", __FUNCTION__);
+ m_xvbaOutput.Dispose();
+ return false;
+ }
+ m_inMsgEvent.Reset();
+
+ return true;
+}
+
+void CDecoder::DestroySession(bool precleanup /*= false*/)
+{
+ // wait for unfinished decoding jobs
+ XbmcThreads::EndTime timer;
+ if (m_xvbaConfig.xvbaSession)
+ {
+ for (unsigned int i = 0; i < m_videoSurfaces.size(); ++i)
+ {
+ xvba_render_state *render = m_videoSurfaces[i];
+ if (render->surface)
+ {
+ XVBA_Surface_Sync_Input syncInput;
+ XVBA_Surface_Sync_Output syncOutput;
+ syncInput.size = sizeof(syncInput);
+ syncInput.session = m_xvbaConfig.xvbaSession;
+ syncInput.surface = render->surface;
+ syncInput.query_status = XVBA_GET_SURFACE_STATUS;
+ syncOutput.size = sizeof(syncOutput);
+ timer.Set(1000);
+ while(!timer.IsTimePast())
+ {
+ if (Success != g_XVBA_vtable.SyncSurface(&syncInput, &syncOutput))
+ {
+ CLog::Log(LOGERROR,"XVBA::DestroySession - failed sync surface");
+ break;
+ }
+ if (!(syncOutput.status_flags & XVBA_STILL_PENDING))
+ break;
+ Sleep(10);
+ }
+ if (timer.IsTimePast())
+ CLog::Log(LOGERROR,"XVBA::DestroySession - unfinished decoding job");
+ }
+ }
+ }
+
+ if (precleanup)
+ {
+ Message *reply;
+ if (m_xvbaOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
+ &reply,
+ 2000))
+ {
+ bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "XVBA::%s - pre-cleanup returned error", __FUNCTION__);
+ m_displayState = XVBA_ERROR;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "XVBA::%s - pre-cleanup timed out", __FUNCTION__);
+ m_displayState = XVBA_ERROR;
+ }
+ }
+ else
+ m_xvbaOutput.Dispose();
+
+ XVBA_Destroy_Decode_Buffers_Input bufInput;
+ bufInput.size = sizeof(bufInput);
+ bufInput.num_of_buffers_in_list = 1;
+ bufInput.session = m_xvbaConfig.xvbaSession;
+
+ for (unsigned int i=0; i<m_xvbaBufferPool.data_control_buffers.size() ; ++i)
+ {
+ bufInput.buffer_list = m_xvbaBufferPool.data_control_buffers[i];
+ g_XVBA_vtable.DestroyDecodeBuffers(&bufInput);
+ }
+ m_xvbaBufferPool.data_control_buffers.clear();
+
+ if (m_xvbaConfig.xvbaSession && m_xvbaBufferPool.data_buffer)
+ {
+ bufInput.buffer_list = m_xvbaBufferPool.data_buffer;
+ g_XVBA_vtable.DestroyDecodeBuffers(&bufInput);
+ }
+ m_xvbaBufferPool.data_buffer = 0;
+
+ if (m_xvbaConfig.xvbaSession && m_xvbaBufferPool.picture_descriptor_buffer)
+ {
+ bufInput.buffer_list = m_xvbaBufferPool.picture_descriptor_buffer;
+ g_XVBA_vtable.DestroyDecodeBuffers(&bufInput);
+ }
+ m_xvbaBufferPool.picture_descriptor_buffer = 0;
+
+ if (m_xvbaConfig.xvbaSession && m_xvbaBufferPool.iq_matrix_buffer)
+ {
+ bufInput.buffer_list = m_xvbaBufferPool.iq_matrix_buffer;
+ g_XVBA_vtable.DestroyDecodeBuffers(&bufInput);
+ }
+ m_xvbaBufferPool.iq_matrix_buffer = 0;
+
+ for (unsigned int i = 0; i < m_videoSurfaces.size(); ++i)
+ {
+ xvba_render_state *render = m_videoSurfaces[i];
+ if (m_xvbaConfig.xvbaSession && render->surface)
+ {
+ g_XVBA_vtable.DestroySurface(render->surface);
+ render->surface = 0;
+ render->state = 0;
+ render->picture_descriptor = 0;
+ render->iq_matrix = 0;
+ }
+ }
+
+ if (m_xvbaConfig.xvbaSession)
+ g_XVBA_vtable.DestroyDecode(m_xvbaConfig.xvbaSession);
+ m_xvbaConfig.xvbaSession = 0;
+}
+
+bool CDecoder::IsSurfaceValid(xvba_render_state *render)
+{
+ // find render state in queue
+ bool found(false);
+ unsigned int i;
+ for(i = 0; i < m_videoSurfaces.size(); ++i)
+ {
+ if(m_videoSurfaces[i] == render)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ CLog::Log(LOGERROR,"%s - video surface not found", __FUNCTION__);
+ return false;
+ }
+ if (m_videoSurfaces[i]->surface == 0)
+ {
+ m_videoSurfaces[i]->state = 0;
+ return false;
+ }
+
+ return true;
+}
+
+bool CDecoder::EnsureDataControlBuffers(unsigned int num)
+{
+ if (m_xvbaBufferPool.data_control_buffers.size() >= num)
+ return true;
+
+ unsigned int missing = num - m_xvbaBufferPool.data_control_buffers.size();
+
+ XVBA_Create_DecodeBuff_Input bufferInput;
+ XVBA_Create_DecodeBuff_Output bufferOutput;
+ bufferInput.size = sizeof(bufferInput);
+ bufferInput.session = m_xvbaConfig.xvbaSession;
+ bufferInput.buffer_type = XVBA_DATA_CTRL_BUFFER;
+ bufferInput.num_of_buffers = 1;
+ bufferOutput.size = sizeof(bufferOutput);
+
+ for (unsigned int i=0; i<missing; ++i)
+ {
+ { CSingleLock lock(m_apiSec);
+ if (Success != g_XVBA_vtable.CreateDecodeBuffers(&bufferInput, &bufferOutput)
+ || bufferOutput.num_of_buffers_in_list != 1)
+ {
+ SetError(__FUNCTION__, "failed to create data control buffer", __LINE__);
+ return false;
+ }
+ }
+ m_xvbaBufferPool.data_control_buffers.push_back(bufferOutput.buffer_list);
+ }
+
+ return true;
+}
+
+void CDecoder::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic)
+{
+ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
+ CDecoder* xvba = (CDecoder*)ctx->GetHardware();
+ unsigned int i;
+
+ CSingleLock lock(xvba->m_decoderSection);
+
+ xvba_render_state * render = NULL;
+ render = (xvba_render_state*)pic->data[0];
+ if(!render)
+ {
+ CLog::Log(LOGERROR, "XVBA::FFReleaseBuffer - invalid context handle provided");
+ return;
+ }
+
+ for(i=0; i<4; i++)
+ pic->data[i]= NULL;
+
+ // find render state in queue
+ if (!xvba->IsSurfaceValid(render))
+ {
+ CLog::Log(LOGDEBUG, "XVBA::FFReleaseBuffer - ignoring invalid buffer");
+ return;
+ }
+
+ render->state &= ~FF_XVBA_STATE_USED_FOR_REFERENCE;
+}
+
+void CDecoder::FFDrawSlice(struct AVCodecContext *avctx,
+ const AVFrame *src, int offset[4],
+ int y, int type, int height)
+{
+ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
+ CDecoder* xvba = (CDecoder*)ctx->GetHardware();
+
+ CSingleLock lock(xvba->m_decoderSection);
+
+ if(xvba->m_displayState != XVBA_OPEN)
+ return;
+
+ if(src->linesize[0] || src->linesize[1] || src->linesize[2]
+ || offset[0] || offset[1] || offset[2])
+ {
+ CLog::Log(LOGERROR, "XVBA::FFDrawSlice - invalid linesizes or offsets provided");
+ return;
+ }
+
+ xvba_render_state * render;
+
+ render = (xvba_render_state*)src->data[0];
+ if(!render)
+ {
+ CLog::Log(LOGERROR, "XVBA::FFDrawSlice - invalid context handle provided");
+ return;
+ }
+
+ // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
+ if (!xvba->IsSurfaceValid(render))
+ {
+ CLog::Log(LOGWARNING, "XVBA::FFDrawSlice - ignoring invalid buffer");
+ return;
+ }
+
+ // decoding
+ XVBA_Decode_Picture_Start_Input startInput;
+ startInput.size = sizeof(startInput);
+ startInput.session = xvba->m_xvbaConfig.xvbaSession;
+ startInput.target_surface = render->surface;
+ { CSingleLock lock(xvba->m_apiSec);
+ if (Success != g_XVBA_vtable.StartDecodePicture(&startInput))
+ {
+ xvba->SetError(__FUNCTION__, "failed to start decoding", __LINE__);
+ return;
+ }
+ }
+
+ XVBA_Decode_Picture_Input picInput;
+ picInput.size = sizeof(picInput);
+ picInput.session = xvba->m_xvbaConfig.xvbaSession;
+ XVBABufferDescriptor *list[2];
+ picInput.buffer_list = list;
+ list[0] = xvba->m_xvbaBufferPool.picture_descriptor_buffer;
+ picInput.num_of_buffers_in_list = 1;
+ if (avctx->codec_id == CODEC_ID_H264)
+ {
+ list[1] = xvba->m_xvbaBufferPool.iq_matrix_buffer;
+ picInput.num_of_buffers_in_list = 2;
+ }
+
+ { CSingleLock lock(xvba->m_apiSec);
+ if (Success != g_XVBA_vtable.DecodePicture(&picInput))
+ {
+ xvba->SetError(__FUNCTION__, "failed to decode picture 1", __LINE__);
+ return;
+ }
+ }
+
+ if (!xvba->EnsureDataControlBuffers(render->num_slices))
+ return;
+
+ XVBADataCtrl *dataControl;
+ int location = 0;
+ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer = 0;
+ for (unsigned int j = 0; j < render->num_slices; ++j)
+ {
+ int startCodeSize = 0;
+ uint8_t startCode[] = {0x00,0x00,0x01};
+ if (avctx->codec_id == CODEC_ID_H264)
+ {
+ startCodeSize = 3;
+ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location,
+ startCode, 3);
+ }
+ else if (avctx->codec_id == CODEC_ID_VC1 &&
+ (memcmp(render->buffers[j].buffer, startCode, 3) != 0))
+ {
+ startCodeSize = 4;
+ uint8_t sdf = 0x0d;
+ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location,
+ startCode, 3);
+ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location+3,
+ &sdf, 1);
+ }
+ // check for potential buffer overwrite
+ unsigned int bytesToCopy = render->buffers[j].size;
+ unsigned int freeBufferSize = xvba->m_xvbaBufferPool.data_buffer->buffer_size -
+ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer;
+ if (bytesToCopy >= freeBufferSize)
+ {
+ xvba->SetError(__FUNCTION__, "bitstream buffer too large, maybe corrupted packet", __LINE__);
+ return;
+ }
+ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location+startCodeSize,
+ render->buffers[j].buffer,
+ render->buffers[j].size);
+ dataControl = (XVBADataCtrl*)xvba->m_xvbaBufferPool.data_control_buffers[j]->bufferXVBA;
+ dataControl->SliceDataLocation = location;
+ dataControl->SliceBytesInBuffer = render->buffers[j].size+startCodeSize;
+ dataControl->SliceBitsInBuffer = dataControl->SliceBytesInBuffer * 8;
+ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer += dataControl->SliceBytesInBuffer;
+ location += dataControl->SliceBytesInBuffer;
+ }
+
+ int bufSize = xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer;
+ int padding = bufSize % 128;
+ if (padding)
+ {
+ padding = 128 - padding;
+ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer += padding;
+ memset((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+bufSize,0,padding);
+ }
+
+ picInput.num_of_buffers_in_list = 2;
+ for (unsigned int i = 0; i < render->num_slices; ++i)
+ {
+ list[0] = xvba->m_xvbaBufferPool.data_buffer;
+ list[0]->data_offset = 0;
+ list[1] = xvba->m_xvbaBufferPool.data_control_buffers[i];
+ list[1]->data_size_in_buffer = sizeof(*dataControl);
+ { CSingleLock lock(xvba->m_apiSec);
+ if (Success != g_XVBA_vtable.DecodePicture(&picInput))
+ {
+ xvba->SetError(__FUNCTION__, "failed to decode picture 2", __LINE__);
+ return;
+ }
+ }
+ }
+ XVBA_Decode_Picture_End_Input endInput;
+ endInput.size = sizeof(endInput);
+ endInput.session = xvba->m_xvbaConfig.xvbaSession;
+ { CSingleLock lock(xvba->m_apiSec);
+ if (Success != g_XVBA_vtable.EndDecodePicture(&endInput))
+ {
+ xvba->SetError(__FUNCTION__, "failed to decode picture 3", __LINE__);
+ return;
+ }
+ }
+
+ // decode sync and error
+ XVBA_Surface_Sync_Input syncInput;
+ XVBA_Surface_Sync_Output syncOutput;
+ syncInput.size = sizeof(syncInput);
+ syncInput.session = xvba->m_xvbaConfig.xvbaSession;
+ syncInput.surface = render->surface;
+ syncInput.query_status = XVBA_GET_SURFACE_STATUS;
+ syncOutput.size = sizeof(syncOutput);
+ int64_t start = CurrentHostCounter();
+ while (1)
+ {
+ { CSingleLock lock(xvba->m_apiSec);
+ if (Success != g_XVBA_vtable.SyncSurface(&syncInput, &syncOutput))
+ {
+ xvba->SetError(__FUNCTION__, "failed sync surface 1", __LINE__);
+ return;
+ }
+ }
+ if (!(syncOutput.status_flags & XVBA_STILL_PENDING))
+ break;
+ if (CurrentHostCounter() - start > CurrentHostFrequency())
+ {
+ xvba->SetError(__FUNCTION__, "timed out waiting for surface", __LINE__);
+ break;
+ }
+ usleep(100);
+ }
+ render->state |= FF_XVBA_STATE_DECODED;
+}
+
+int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic)
+{
+ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
+ CDecoder* xvba = (CDecoder*)ctx->GetHardware();
+
+ pic->data[0] =
+ pic->data[1] =
+ pic->data[2] =
+ pic->data[3] = 0;
+
+ pic->linesize[0] =
+ pic->linesize[1] =
+ pic->linesize[2] =
+ pic->linesize[3] = 0;
+
+ CSingleLock lock(xvba->m_decoderSection);
+
+ if(xvba->m_displayState != XVBA_OPEN)
+ return -1;
+
+ if (xvba->m_xvbaConfig.xvbaSession == 0)
+ {
+ if (!xvba->CreateSession(avctx))
+ return -1;
+ }
+
+ xvba_render_state * render = NULL;
+ // find unused surface
+ { CSingleLock lock(xvba->m_videoSurfaceSec);
+ for(unsigned int i = 0; i < xvba->m_videoSurfaces.size(); ++i)
+ {
+ if(!(xvba->m_videoSurfaces[i]->state & (FF_XVBA_STATE_USED_FOR_REFERENCE | FF_XVBA_STATE_USED_FOR_RENDER)))
+ {
+ render = xvba->m_videoSurfaces[i];
+ render->state = 0;
+ break;
+ }
+ }
+ }
+
+ // create a new render state
+ if (render == NULL)
+ {
+ render = (xvba_render_state*)calloc(sizeof(xvba_render_state), 1);
+ if (render == NULL)
+ {
+ CLog::Log(LOGERROR, "XVBA::FFGetBuffer - calloc failed");
+ return -1;
+ }
+ render->surface = 0;
+ render->buffers_alllocated = 0;
+ CSingleLock lock(xvba->m_videoSurfaceSec);
+ xvba->m_videoSurfaces.push_back(render);
+ }
+
+ // create a new surface
+ if (render->surface == 0)
+ {
+ XVBA_Create_Surface_Input surfaceInput;
+ XVBA_Create_Surface_Output surfaceOutput;
+ surfaceInput.size = sizeof(surfaceInput);
+ surfaceInput.surface_type = xvba->m_xvbaConfig.decoderCap.surface_type;
+ surfaceInput.width = xvba->m_xvbaConfig.surfaceWidth;
+ surfaceInput.height = xvba->m_xvbaConfig.surfaceHeight;
+ surfaceInput.session = xvba->m_xvbaConfig.xvbaSession;
+ surfaceOutput.size = sizeof(surfaceOutput);
+ { CSingleLock lock(xvba->m_apiSec);
+ if (Success != g_XVBA_vtable.CreateSurface(&surfaceInput, &surfaceOutput))
+ {
+ xvba->SetError(__FUNCTION__, "failed to create video surface", __LINE__);
+ return -1;
+ }
+ }
+ render->surface = surfaceOutput.surface;
+ render->picture_descriptor = (XVBAPictureDescriptor *)xvba->m_xvbaBufferPool.picture_descriptor_buffer->bufferXVBA;
+ render->iq_matrix = (XVBAQuantMatrixAvc *)xvba->m_xvbaBufferPool.iq_matrix_buffer->bufferXVBA;
+ CLog::Log(LOGDEBUG, "XVBA::FFGetBuffer - created video surface");
+ }
+
+ if (render == NULL)
+ return -1;
+
+ pic->data[0] = (uint8_t*)render;
+
+ pic->type= FF_BUFFER_TYPE_USER;
+
+ render->state |= FF_XVBA_STATE_USED_FOR_REFERENCE;
+ render->state &= ~FF_XVBA_STATE_DECODED;
+ pic->reordered_opaque= avctx->reordered_opaque;
+
+ return 0;
+}
+
+int CDecoder::Decode(AVCodecContext* avctx, AVFrame* frame)
+{
+ int result = Check(avctx);
+ if (result)
+ return result;
+
+ CSingleLock lock(m_decoderSection);
+
+ if(frame)
+ { // we have a new frame from decoder
+
+ xvba_render_state * render = (xvba_render_state*)frame->data[0];
+ if(!render)
+ {
+ CLog::Log(LOGERROR, "XVBA::Decode - no render buffer");
+ return VC_ERROR;
+ }
+
+ // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
+ if (!IsSurfaceValid(render))
+ {
+ CLog::Log(LOGWARNING, "XVBA::Decode - ignoring invalid buffer");
+ return VC_BUFFER;
+ }
+ if (!(render->state & FF_XVBA_STATE_DECODED))
+ {
+ CLog::Log(LOGDEBUG, "XVBA::Decode - ffmpeg failed");
+ return VC_BUFFER;
+ }
+
+ CSingleLock lock(m_videoSurfaceSec);
+ render->state |= FF_XVBA_STATE_USED_FOR_RENDER;
+ lock.Leave();
+
+ // send frame to output for processing
+ CXvbaDecodedPicture pic;
+ memset(&pic.DVDPic, 0, sizeof(pic.DVDPic));
+ ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&pic.DVDPic);
+ pic.render = render;
+ m_bufferStats.IncDecoded();
+ m_xvbaOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, &pic, sizeof(pic));
+
+ m_codecControl = pic.DVDPic.iFlags & (DVP_FLAG_DRAIN | DVP_FLAG_NO_POSTPROC);
+ }
+
+ int retval = 0;
+ uint16_t decoded, processed, render;
+ Message *msg;
+ while (m_xvbaOutput.m_controlPort.ReceiveInMessage(&msg))
+ {
+ if (msg->signal == COutputControlProtocol::ERROR)
+ {
+ m_displayState = XVBA_ERROR;
+ retval |= VC_ERROR;
+ }
+ msg->Release();
+ }
+
+ m_bufferStats.Get(decoded, processed, render);
+
+ uint64_t startTime = CurrentHostCounter();
+ while (!retval)
+ {
+ if (m_xvbaOutput.m_dataPort.ReceiveInMessage(&msg))
+ {
+ if (msg->signal == COutputDataProtocol::PICTURE)
+ {
+ if (m_presentPicture)
+ {
+ m_presentPicture->ReturnUnused();
+ m_presentPicture = 0;
+ }
+
+ m_presentPicture = *(CXvbaRenderPicture**)msg->data;
+ m_presentPicture->xvba = this;
+ m_bufferStats.DecRender();
+ m_bufferStats.Get(decoded, processed, render);
+ retval |= VC_PICTURE;
+ }
+ msg->Release();
+ }
+ else if (m_xvbaOutput.m_controlPort.ReceiveInMessage(&msg))
+ {
+ if (msg->signal == COutputControlProtocol::STATS)
+ {
+ m_bufferStats.Get(decoded, processed, render);
+ }
+ else
+ {
+ m_displayState = XVBA_ERROR;
+ retval |= VC_ERROR;
+ }
+ msg->Release();
+ }
+
+ if ((m_codecControl & DVP_FLAG_DRAIN))
+ {
+ if (decoded + processed + render < 2)
+ {
+ retval |= VC_BUFFER;
+ }
+ }
+ else
+ {
+ if (decoded + processed + render < 4)
+ {
+ retval |= VC_BUFFER;
+ }
+ }
+
+ if (!retval && !m_inMsgEvent.WaitMSec(2000))
+ break;
+ }
+ uint64_t diff = CurrentHostCounter() - startTime;
+ if (retval & VC_PICTURE)
+ {
+ m_bufferStats.SetParams(diff, m_speed);
+ if (diff*1000/CurrentHostFrequency() > 50)
+ CLog::Log(LOGDEBUG,"XVBA::Decode long wait: %d", (int)((diff*1000)/CurrentHostFrequency()));
+ }
+
+ if (!retval)
+ {
+ CLog::Log(LOGERROR, "XVBA::%s - timed out waiting for output message", __FUNCTION__);
+ m_displayState = XVBA_ERROR;
+ retval |= VC_ERROR;
+ }
+
+ return retval;
+
+}
+
+bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
+{
+ CSingleLock lock(m_decoderSection);
+
+ if (m_displayState != XVBA_OPEN)
+ return false;
+
+ *picture = m_presentPicture->DVDPic;
+ picture->xvba = m_presentPicture;
+
+ return true;
+}
+
+void CDecoder::ReturnRenderPicture(CXvbaRenderPicture *renderPic)
+{
+ m_xvbaOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
+}
+
+
+//void CDecoder::CopyYV12(int index, uint8_t *dest)
+//{
+// CSharedLock lock(m_decoderSection);
+//
+// { CSharedLock dLock(m_displaySection);
+// if(m_displayState != XVBA_OPEN)
+// return;
+// }
+//
+// if (!m_flipBuffer[index].outPic)
+// {
+// CLog::Log(LOGWARNING, "XVBA::Present: present picture is NULL");
+// return;
+// }
+//
+// XVBA_GetSurface_Target target;
+// target.size = sizeof(target);
+// target.surfaceType = XVBA_YV12;
+// target.flag = XVBA_FRAME;
+//
+// XVBA_Get_Surface_Input input;
+// input.size = sizeof(input);
+// input.session = m_xvbaSession;
+// input.src_surface = m_flipBuffer[index].outPic->render->surface;
+// input.target_buffer = dest;
+// input.target_pitch = m_surfaceWidth;
+// input.target_width = m_surfaceWidth;
+// input.target_height = m_surfaceHeight;
+// input.target_parameter = target;
+// { CSingleLock lock(m_apiSec);
+// if (Success != g_XVBA_vtable.GetSurface(&input))
+// {
+// CLog::Log(LOGERROR,"(XVBA::CopyYV12) failed to get surface");
+// }
+// }
+//}
+
+void CDecoder::Reset()
+{
+ CSingleLock lock(m_decoderSection);
+
+ if (!m_xvbaConfig.xvbaSession)
+ return;
+
+ Message *reply;
+ if (m_xvbaOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
+ &reply,
+ 2000))
+ {
+ bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "XVBA::%s - flush returned error", __FUNCTION__);
+ m_displayState = XVBA_ERROR;
+ }
+ else
+ m_bufferStats.Reset();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "XVBA::%s - flush timed out", __FUNCTION__);
+ m_displayState = XVBA_ERROR;
+ }
+}
+
+bool CDecoder::CanSkipDeint()
+{
+ return m_bufferStats.CanSkipDeint();
+}
+
+void CDecoder::SetSpeed(int speed)
+{
+ m_speed = speed;
+}
+
+//-----------------------------------------------------------------------------
+// RenderPicture
+//-----------------------------------------------------------------------------
+
+CXvbaRenderPicture* CXvbaRenderPicture::Acquire()
+{
+ CSingleLock lock(*renderPicSection);
+
+ if (refCount == 0)
+ xvba->Acquire();
+
+ refCount++;
+ return this;
+}
+
+long CXvbaRenderPicture::Release()
+{
+ CSingleLock lock(*renderPicSection);
+
+ refCount--;
+ if (refCount > 0)
+ return refCount;
+
+ lock.Leave();
+ xvba->ReturnRenderPicture(this);
+ xvba->ReleasePicReference();
+
+ return refCount;
+}
+
+void CXvbaRenderPicture::ReturnUnused()
+{
+ { CSingleLock lock(*renderPicSection);
+ if (refCount > 0)
+ return;
+ }
+ if (xvba)
+ xvba->ReturnRenderPicture(this);
+}
+
+//-----------------------------------------------------------------------------
+// Output
+//-----------------------------------------------------------------------------
+COutput::COutput(CEvent *inMsgEvent) :
+ CThread("XVBA Output Thread"),
+ m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
+ m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent)
+{
+ m_inMsgEvent = inMsgEvent;
+
+ CXvbaRenderPicture pic;
+ pic.renderPicSection = &m_bufferPool.renderPicSec;
+ pic.refCount = 0;
+ for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
+ {
+ m_bufferPool.allRenderPics.push_back(pic);
+ }
+ for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); ++i)
+ {
+ m_bufferPool.freeRenderPics.push_back(&m_bufferPool.allRenderPics[i]);
+ }
+}
+
+void COutput::Start()
+{
+ Create();
+}
+
+COutput::~COutput()
+{
+ Dispose();
+
+ m_bufferPool.freeRenderPics.clear();
+ m_bufferPool.usedRenderPics.clear();
+ m_bufferPool.allRenderPics.clear();
+}
+
+void COutput::Dispose()
+{
+ CSingleLock lock(g_graphicsContext);
+ m_bStop = true;
+ m_outMsgEvent.Set();
+ StopThread();
+ m_controlPort.Purge();
+ m_dataPort.Purge();
+}
+
+void COutput::OnStartup()
+{
+ CLog::Log(LOGNOTICE, "COutput::OnStartup: Output Thread created");
+}
+
+void COutput::OnExit()
+{
+ CLog::Log(LOGNOTICE, "COutput::OnExit: Output Thread terminated");
+}
+
+enum OUTPUT_STATES
+{
+ O_TOP = 0, // 0
+ O_TOP_ERROR, // 1
+ O_TOP_UNCONFIGURED, // 2
+ O_TOP_CONFIGURED, // 3
+ O_TOP_CONFIGURED_WAIT_RES1, // 4
+ O_TOP_CONFIGURED_WAIT_DEC, // 5
+ O_TOP_CONFIGURED_STEP1, // 6
+ O_TOP_CONFIGURED_WAIT_RES2, // 7
+ O_TOP_CONFIGURED_STEP2, // 8
+};
+
+int OUTPUT_parentStates[] = {
+ -1,
+ 0, //TOP_ERROR
+ 0, //TOP_UNCONFIGURED
+ 0, //TOP_CONFIGURED
+ 3, //TOP_CONFIGURED_WAIT_RES1
+ 3, //TOP_CONFIGURED_WAIT_DEC
+ 3, //TOP_CONFIGURED_STEP1
+ 3, //TOP_CONFIGURED_WAIT_RES2
+ 3, //TOP_CONFIGURED_STEP2
+};
+
+void COutput::StateMachine(int signal, Protocol *port, Message *msg)
+{
+ for (int state = m_state; ; state = OUTPUT_parentStates[state])
+ {
+ switch (state)
+ {
+ case O_TOP: // TOP
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::FLUSH:
+ msg->Reply(COutputControlProtocol::ACC);
+ return;
+ case COutputControlProtocol::PRECLEANUP:
+ msg->Reply(COutputControlProtocol::ACC);
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case COutputDataProtocol::RETURNPIC:
+ CXvbaRenderPicture *pic;
+ pic = *((CXvbaRenderPicture**)msg->data);
+ ProcessReturnPicture(pic);
+ return;
+ default:
+ break;
+ }
+ }
+ {
+ std::string portName = port == NULL ? "timer" : port->portName;
+ CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
+ }
+ return;
+
+ case O_TOP_ERROR:
+ m_extTimeout = 1000;
+ break;
+
+ case O_TOP_UNCONFIGURED:
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::INIT:
+ CXvbaConfig *data;
+ data = (CXvbaConfig*)msg->data;
+ if (data)
+ {
+ m_config = *data;
+ }
+ Init();
+ EnsureBufferPool();
+ if (!m_xvbaError)
+ {
+ m_state = O_TOP_CONFIGURED_WAIT_RES1;
+ msg->Reply(COutputControlProtocol::ACC);
+ }
+ else
+ {
+ m_state = O_TOP_ERROR;
+ msg->Reply(COutputControlProtocol::ERROR);
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case O_TOP_CONFIGURED:
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::FLUSH:
+ m_state = O_TOP_CONFIGURED_WAIT_RES1;
+ Flush();
+ msg->Reply(COutputControlProtocol::ACC);
+ return;
+ case COutputControlProtocol::PRECLEANUP:
+ m_state = O_TOP_UNCONFIGURED;
+ m_extTimeout = 10000;
+ Flush();
+ ReleaseBufferPool(true);
+ msg->Reply(COutputControlProtocol::ACC);
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case COutputDataProtocol::NEWFRAME:
+ CXvbaDecodedPicture *frame;
+ frame = (CXvbaDecodedPicture*)msg->data;
+ if (frame)
+ {
+ m_decodedPics.push(*frame);
+ m_extTimeout = 0;
+ }
+ return;
+ case COutputDataProtocol::RETURNPIC:
+ CXvbaRenderPicture *pic;
+ pic = *((CXvbaRenderPicture**)msg->data);
+ ProcessReturnPicture(pic);
+ m_controlPort.SendInMessage(COutputControlProtocol::STATS);
+ m_extTimeout = 0;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case O_TOP_CONFIGURED_WAIT_RES1:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::TIMEOUT:
+ if (!m_decodedPics.empty() && FindFreeSurface() >= 0 && !m_bufferPool.freeRenderPics.empty())
+ {
+ m_state = O_TOP_CONFIGURED_WAIT_DEC;
+ m_bStateMachineSelfTrigger = true;
+ }
+ else
+ {
+ if (m_extTimeout != 0)
+ {
+ uint16_t decoded, processed, render;
+ m_config.stats->Get(decoded, processed, render);
+// CLog::Log(LOGDEBUG, "CVDPAU::COutput - timeout idle: decoded: %d, proc: %d, render: %d", decoded, processed, render);
+ }
+ m_extTimeout = 100;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case O_TOP_CONFIGURED_WAIT_DEC:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::TIMEOUT:
+ if (IsDecodingFinished())
+ {
+ m_state = O_TOP_CONFIGURED_STEP1;
+ m_bStateMachineSelfTrigger = true;
+ }
+ else
+ {
+ m_extTimeout = 1;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case O_TOP_CONFIGURED_STEP1:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::TIMEOUT:
+ m_processPicture = m_decodedPics.front();
+ m_decodedPics.pop();
+ InitCycle();
+ CXvbaRenderPicture *pic;
+ pic = ProcessPicture();
+ if (pic)
+ {
+ m_config.stats->IncRender();
+ m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic));
+ }
+ if (m_xvbaError)
+ {
+ m_state = O_TOP_ERROR;
+ return;
+ }
+ if (m_deinterlacing && !m_deintSkip)
+ {
+ m_state = O_TOP_CONFIGURED_WAIT_RES2;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ FiniCycle();
+ m_state = O_TOP_CONFIGURED_WAIT_RES1;
+ m_extTimeout = 0;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case O_TOP_CONFIGURED_WAIT_RES2:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::TIMEOUT:
+ if (FindFreeSurface() >= 0 && !m_bufferPool.freeRenderPics.empty())
+ {
+ m_state = O_TOP_CONFIGURED_STEP2;
+ m_bStateMachineSelfTrigger = true;
+ }
+ else
+ {
+ if (m_extTimeout != 0)
+ {
+ uint16_t decoded, processed, render;
+ m_config.stats->Get(decoded, processed, render);
+ CLog::Log(LOGDEBUG, "CVDPAU::COutput - timeout idle: decoded: %d, proc: %d, render: %d", decoded, processed, render);
+ }
+ m_extTimeout = 100;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case O_TOP_CONFIGURED_STEP2:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case COutputControlProtocol::TIMEOUT:
+ CXvbaRenderPicture *pic;
+ m_deintStep = 1;
+ pic = ProcessPicture();
+ if (pic)
+ {
+ m_config.stats->IncRender();
+ m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic));
+ }
+ if (m_xvbaError)
+ {
+ m_state = O_TOP_ERROR;
+ return;
+ }
+ FiniCycle();
+ m_state = O_TOP_CONFIGURED_WAIT_RES1;
+ m_extTimeout = 0;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default: // we are in no state, should not happen
+ CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
+ return;
+ }
+ } // for
+}
+
+void COutput::Process()
+{
+ Message *msg;
+ Protocol *port;
+ bool gotMsg;
+
+ m_state = O_TOP_UNCONFIGURED;
+ m_extTimeout = 1000;
+ m_bStateMachineSelfTrigger = false;
+
+ while (!m_bStop)
+ {
+ gotMsg = false;
+
+ if (m_bStateMachineSelfTrigger)
+ {
+ m_bStateMachineSelfTrigger = false;
+ // self trigger state machine
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ continue;
+ }
+ // check control port
+ else if (m_controlPort.ReceiveOutMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_controlPort;
+ }
+ // check data port
+ else if (m_dataPort.ReceiveOutMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_dataPort;
+ }
+ if (gotMsg)
+ {
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ continue;
+ }
+
+ // wait for message
+ else if (m_outMsgEvent.WaitMSec(m_extTimeout))
+ {
+ continue;
+ }
+ // time out
+ else
+ {
+ msg = m_controlPort.GetMessage();
+ msg->signal = COutputControlProtocol::TIMEOUT;
+ port = 0;
+ // signal timeout to state machine
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ }
+ }
+ Flush();
+ Uninit();
+}
+
+bool COutput::Init()
+{
+ if (!CreateGlxContext())
+ return false;
+
+ m_xvbaError = false;
+ m_processPicture.render = 0;
+ m_fence = None;
+
+ return true;
+}
+
+bool COutput::Uninit()
+{
+ ReleaseBufferPool();
+ DestroyGlxContext();
+ return true;
+}
+
+void COutput::Flush()
+{
+ while (!m_decodedPics.empty())
+ {
+ CXvbaDecodedPicture pic = m_decodedPics.front();
+ m_decodedPics.pop();
+ CSingleLock lock(*m_config.videoSurfaceSec);
+ if (pic.render)
+ pic.render->state &= ~(FF_XVBA_STATE_USED_FOR_RENDER | FF_XVBA_STATE_DECODED);