PageRenderTime 163ms CodeModel.GetById 23ms app.highlight 125ms RepoModel.GetById 1ms app.codeStats 1ms

/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp

http://github.com/xbmc/xbmc
C++ | 3624 lines | 3084 code | 410 blank | 130 comment | 596 complexity | 8e1d48e662ca0fb9bcbbc97751e74240 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 *      Copyright (C) 2005-2013 Team XBMC
   3 *      http://xbmc.org
   4 *
   5 *  This Program is free software; you can redistribute it and/or modify
   6 *  it under the terms of the GNU General Public License as published by
   7 *  the Free Software Foundation; either version 2, or (at your option)
   8 *  any later version.
   9 *
  10 *  This Program is distributed in the hope that it will be useful,
  11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 *  GNU General Public License for more details.
  14 *
  15 *  You should have received a copy of the GNU General Public License
  16 *  along with XBMC; see the file COPYING.  If not, see
  17 *  <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "system.h"
  22#ifdef HAVE_LIBVDPAU
  23#include <dlfcn.h>
  24#include "windowing/WindowingFactory.h"
  25#include "VDPAU.h"
  26#include "guilib/GraphicContext.h"
  27#include "guilib/TextureManager.h"
  28#include "cores/VideoRenderers/RenderManager.h"
  29#include "DVDVideoCodecFFmpeg.h"
  30#include "DVDClock.h"
  31#include "settings/Settings.h"
  32#include "settings/AdvancedSettings.h"
  33#include "settings/MediaSettings.h"
  34#include "Application.h"
  35#include "utils/MathUtils.h"
  36#include "utils/TimeUtils.h"
  37#include "DVDCodecs/DVDCodecUtils.h"
  38#include "cores/VideoRenderers/RenderFlags.h"
  39
  40using namespace VDPAU;
  41#define NUM_RENDER_PICS 7
  42
  43#define ARSIZE(x) (sizeof(x) / sizeof((x)[0]))
  44
  45CDecoder::Desc decoder_profiles[] = {
  46{"MPEG1",        VDP_DECODER_PROFILE_MPEG1},
  47{"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE},
  48{"MPEG2_MAIN",   VDP_DECODER_PROFILE_MPEG2_MAIN},
  49{"H264_BASELINE",VDP_DECODER_PROFILE_H264_BASELINE},
  50{"H264_MAIN",    VDP_DECODER_PROFILE_H264_MAIN},
  51{"H264_HIGH",    VDP_DECODER_PROFILE_H264_HIGH},
  52{"VC1_SIMPLE",   VDP_DECODER_PROFILE_VC1_SIMPLE},
  53{"VC1_MAIN",     VDP_DECODER_PROFILE_VC1_MAIN},
  54{"VC1_ADVANCED", VDP_DECODER_PROFILE_VC1_ADVANCED},
  55#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
  56{"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP},
  57#endif
  58};
  59const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CDecoder::Desc);
  60
  61static struct SInterlaceMapping
  62{
  63  const EINTERLACEMETHOD     method;
  64  const VdpVideoMixerFeature feature;
  65} g_interlace_mapping[] = 
  66{ {VS_INTERLACEMETHOD_VDPAU_TEMPORAL             , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
  67, {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF        , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
  68, {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL     , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
  69, {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
  70, {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE     , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE}
  71, {VS_INTERLACEMETHOD_NONE                       , (VdpVideoMixerFeature)-1}
  72};
  73
  74static float studioCSCKCoeffs601[3] = {0.299, 0.587, 0.114}; //BT601 {Kr, Kg, Kb}
  75static float studioCSCKCoeffs709[3] = {0.2126, 0.7152, 0.0722}; //BT709 {Kr, Kg, Kb}
  76
  77//-----------------------------------------------------------------------------
  78//-----------------------------------------------------------------------------
  79
  80CVDPAUContext *CVDPAUContext::m_context = 0;
  81CCriticalSection CVDPAUContext::m_section;
  82Display *CVDPAUContext::m_display = 0;
  83void *CVDPAUContext::m_dlHandle = 0;
  84
  85CVDPAUContext::CVDPAUContext()
  86{
  87  m_context = 0;
  88  m_refCount = 0;
  89}
  90
  91void CVDPAUContext::Release()
  92{
  93  CSingleLock lock(m_section);
  94
  95  m_refCount--;
  96  if (m_refCount <= 0)
  97  {
  98    Close();
  99    delete this;
 100    m_context = 0;
 101  }
 102}
 103
 104void CVDPAUContext::Close()
 105{
 106  CLog::Log(LOGNOTICE, "VDPAU::Close - closing decoder context");
 107  DestroyContext();
 108}
 109
 110bool CVDPAUContext::EnsureContext(CVDPAUContext **ctx)
 111{
 112  CSingleLock lock(m_section);
 113
 114  if (m_context)
 115  {
 116    m_context->m_refCount++;
 117    *ctx = m_context;
 118    return true;
 119  }
 120
 121  m_context = new CVDPAUContext();
 122  *ctx = m_context;
 123  {
 124    CSingleLock gLock(g_graphicsContext);
 125    if (!m_context->LoadSymbols() || !m_context->CreateContext())
 126    {
 127      delete m_context;
 128      m_context = 0;
 129      return false;
 130    }
 131  }
 132
 133  m_context->m_refCount++;
 134
 135  *ctx = m_context;
 136  return true;
 137}
 138
 139VDPAU_procs& CVDPAUContext::GetProcs()
 140{
 141  return m_vdpProcs;
 142}
 143
 144VdpVideoMixerFeature* CVDPAUContext::GetFeatures()
 145{
 146  return m_vdpFeatures;
 147}
 148
 149int CVDPAUContext::GetFeatureCount()
 150{
 151  return m_featureCount;
 152}
 153
 154bool CVDPAUContext::LoadSymbols()
 155{
 156  if (!m_dlHandle)
 157  {
 158    m_dlHandle  = dlopen("libvdpau.so.1", RTLD_LAZY);
 159    if (!m_dlHandle)
 160    {
 161      const char* error = dlerror();
 162      if (!error)
 163        error = "dlerror() returned NULL";
 164
 165      CLog::Log(LOGERROR,"VDPAU::LoadSymbols: Unable to get handle to lib: %s", error);
 166      return false;
 167    }
 168  }
 169
 170  char* error;
 171  (void)dlerror();
 172  dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(m_dlHandle, (const char*)"vdp_device_create_x11");
 173  error = dlerror();
 174  if (error)
 175  {
 176    CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
 177    m_vdpDevice = VDP_INVALID_HANDLE;
 178    return false;
 179  }
 180  return true;
 181}
 182
 183bool CVDPAUContext::CreateContext()
 184{
 185  CLog::Log(LOGNOTICE,"VDPAU::CreateContext - creating decoder context");
 186
 187  int mScreen;
 188  { CSingleLock lock(g_graphicsContext);
 189    if (!m_display)
 190      m_display = XOpenDisplay(NULL);
 191    mScreen = g_Windowing.GetCurrentScreen();
 192  }
 193
 194  VdpStatus vdp_st;
 195  // Create Device
 196  vdp_st = dl_vdp_device_create_x11(m_display,
 197                                    mScreen,
 198                                   &m_vdpDevice,
 199                                   &m_vdpProcs.vdp_get_proc_address);
 200
 201  CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",m_vdpDevice,vdp_st);
 202  if (vdp_st != VDP_STATUS_OK)
 203  {
 204    CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x.  Falling back.",vdp_st);
 205    m_vdpDevice = VDP_INVALID_HANDLE;
 206    return false;
 207  }
 208
 209  QueryProcs();
 210  SpewHardwareAvailable();
 211  return true;
 212}
 213
 214void CVDPAUContext::QueryProcs()
 215{
 216  VdpStatus vdp_st;
 217
 218#define VDP_PROC(id, proc) \
 219  do { \
 220    vdp_st = m_vdpProcs.vdp_get_proc_address(m_vdpDevice, id, (void**)&proc); \
 221    if (vdp_st != VDP_STATUS_OK) \
 222    { \
 223      CLog::Log(LOGERROR, "CVDPAUContext::GetProcs - failed to get proc id"); \
 224    } \
 225  } while(0);
 226
 227  VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING                    , m_vdpProcs.vdp_get_error_string);
 228  VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY                      , m_vdpProcs.vdp_device_destroy);
 229  VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX                 , m_vdpProcs.vdp_generate_csc_matrix);
 230  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE                , m_vdpProcs.vdp_video_surface_create);
 231  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY               , m_vdpProcs.vdp_video_surface_destroy);
 232  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_put_bits_y_cb_cr);
 233  VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR      , m_vdpProcs.vdp_video_surface_get_bits_y_cb_cr);
 234  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR     , m_vdpProcs.vdp_output_surface_put_bits_y_cb_cr);
 235  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_put_bits_native);
 236  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE               , m_vdpProcs.vdp_output_surface_create);
 237  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY              , m_vdpProcs.vdp_output_surface_destroy);
 238  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE      , m_vdpProcs.vdp_output_surface_get_bits_native);
 239  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpProcs.vdp_output_surface_render_output_surface);
 240  VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED     , m_vdpProcs.vdp_output_surface_put_bits_indexed);
 241  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE                  , m_vdpProcs.vdp_video_mixer_create);
 242  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES     , m_vdpProcs.vdp_video_mixer_set_feature_enables);
 243  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY                 , m_vdpProcs.vdp_video_mixer_destroy);
 244  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER                  , m_vdpProcs.vdp_video_mixer_render);
 245  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES    , m_vdpProcs.vdp_video_mixer_set_attribute_values);
 246  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpProcs.vdp_video_mixer_query_parameter_support);
 247  VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT   , m_vdpProcs.vdp_video_mixer_query_feature_support);
 248  VDP_PROC(VDP_FUNC_ID_DECODER_CREATE                      , m_vdpProcs.vdp_decoder_create);
 249  VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY                     , m_vdpProcs.vdp_decoder_destroy);
 250  VDP_PROC(VDP_FUNC_ID_DECODER_RENDER                      , m_vdpProcs.vdp_decoder_render);
 251  VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES          , m_vdpProcs.vdp_decoder_query_caps);
 252#undef VDP_PROC
 253}
 254
 255VdpDevice CVDPAUContext::GetDevice()
 256{
 257  return m_vdpDevice;
 258}
 259
 260void CVDPAUContext::DestroyContext()
 261{
 262  if (!m_vdpProcs.vdp_device_destroy)
 263    return;
 264
 265  m_vdpProcs.vdp_device_destroy(m_vdpDevice);
 266  m_vdpDevice = VDP_INVALID_HANDLE;
 267}
 268
 269void CVDPAUContext::SpewHardwareAvailable()  //CopyrighVDPAUt (c) 2008 Wladimir J. van der Laan  -- VDPInfo
 270{
 271  VdpStatus rv;
 272  CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:");
 273  CLog::Log(LOGNOTICE,"name          level macbs width height");
 274  CLog::Log(LOGNOTICE,"------------------------------------");
 275  for(unsigned int x=0; x<decoder_profile_count; ++x)
 276  {
 277    VdpBool is_supported = false;
 278    uint32_t max_level, max_macroblocks, max_width, max_height;
 279    rv = m_vdpProcs.vdp_decoder_query_caps(m_vdpDevice, decoder_profiles[x].id,
 280                                &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
 281    if(rv == VDP_STATUS_OK && is_supported)
 282    {
 283      CLog::Log(LOGNOTICE,"%-16s %2i %5i %5i %5i\n", decoder_profiles[x].name,
 284                max_level, max_macroblocks, max_width, max_height);
 285    }
 286  }
 287  CLog::Log(LOGNOTICE,"------------------------------------");
 288  m_featureCount = 0;
 289#define CHECK_SUPPORT(feature)  \
 290  do { \
 291    VdpBool supported; \
 292    if(m_vdpProcs.vdp_video_mixer_query_feature_support(m_vdpDevice, feature, &supported) == VDP_STATUS_OK && supported) { \
 293      CLog::Log(LOGNOTICE, "Mixer feature: "#feature);  \
 294      m_vdpFeatures[m_featureCount++] = feature; \
 295    } \
 296  } while(false)
 297
 298  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
 299  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
 300  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
 301  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
 302  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
 303#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
 304  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
 305  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
 306  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
 307  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
 308  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
 309  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
 310  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
 311  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
 312  CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
 313#endif
 314#undef CHECK_SUPPORT
 315}
 316
 317bool CVDPAUContext::Supports(VdpVideoMixerFeature feature)
 318{
 319  for(int i = 0; i < m_featureCount; i++)
 320  {
 321    if(m_vdpFeatures[i] == feature)
 322      return true;
 323  }
 324  return false;
 325}
 326
 327//-----------------------------------------------------------------------------
 328// VDPAU Video Surface states
 329//-----------------------------------------------------------------------------
 330
 331#define SURFACE_USED_FOR_REFERENCE 0x01
 332#define SURFACE_USED_FOR_RENDER    0x02
 333
 334void CVideoSurfaces::AddSurface(VdpVideoSurface surf)
 335{
 336  CSingleLock lock(m_section);
 337  m_state[surf] = SURFACE_USED_FOR_REFERENCE;
 338}
 339
 340void CVideoSurfaces::ClearReference(VdpVideoSurface surf)
 341{
 342  CSingleLock lock(m_section);
 343  if (m_state.find(surf) == m_state.end())
 344  {
 345    CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
 346    return;
 347  }
 348  m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
 349  if (m_state[surf] == 0)
 350  {
 351    m_freeSurfaces.push_back(surf);
 352  }
 353}
 354
 355bool CVideoSurfaces::MarkRender(VdpVideoSurface surf)
 356{
 357  CSingleLock lock(m_section);
 358  if (m_state.find(surf) == m_state.end())
 359  {
 360    CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
 361    return false;
 362  }
 363  std::list<VdpVideoSurface>::iterator it;
 364  it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
 365  if (it != m_freeSurfaces.end())
 366  {
 367    m_freeSurfaces.erase(it);
 368  }
 369  m_state[surf] |= SURFACE_USED_FOR_RENDER;
 370  return true;
 371}
 372
 373void CVideoSurfaces::ClearRender(VdpVideoSurface surf)
 374{
 375  CSingleLock lock(m_section);
 376  if (m_state.find(surf) == m_state.end())
 377  {
 378    CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
 379    return;
 380  }
 381  m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
 382  if (m_state[surf] == 0)
 383  {
 384    m_freeSurfaces.push_back(surf);
 385  }
 386}
 387
 388bool CVideoSurfaces::IsValid(VdpVideoSurface surf)
 389{
 390  CSingleLock lock(m_section);
 391  if (m_state.find(surf) != m_state.end())
 392    return true;
 393  else
 394    return false;
 395}
 396
 397VdpVideoSurface CVideoSurfaces::GetFree(VdpVideoSurface surf)
 398{
 399  CSingleLock lock(m_section);
 400  if (m_state.find(surf) != m_state.end())
 401  {
 402    std::list<VdpVideoSurface>::iterator it;
 403    it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
 404    if (it == m_freeSurfaces.end())
 405    {
 406      CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
 407    }
 408    else
 409    {
 410      m_freeSurfaces.erase(it);
 411      m_state[surf] = SURFACE_USED_FOR_REFERENCE;
 412      return surf;
 413    }
 414  }
 415
 416  if (!m_freeSurfaces.empty())
 417  {
 418    VdpVideoSurface freeSurf = m_freeSurfaces.front();
 419    m_freeSurfaces.pop_front();
 420    m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
 421    return freeSurf;
 422  }
 423
 424  return VDP_INVALID_HANDLE;
 425}
 426
 427VdpVideoSurface CVideoSurfaces::GetAtIndex(int idx)
 428{
 429  if (idx >= m_state.size())
 430    return VDP_INVALID_HANDLE;
 431
 432  std::map<VdpVideoSurface, int>::iterator it = m_state.begin();
 433  for(int i = 0; i < idx; i++)
 434    ++it;
 435  return it->first;
 436}
 437
 438VdpVideoSurface CVideoSurfaces::RemoveNext(bool skiprender)
 439{
 440  CSingleLock lock(m_section);
 441  VdpVideoSurface surf;
 442  std::map<VdpVideoSurface, int>::iterator it;
 443  for(it = m_state.begin(); it != m_state.end(); ++it)
 444  {
 445    if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
 446      continue;
 447    surf = it->first;
 448    m_state.erase(surf);
 449
 450    std::list<VdpVideoSurface>::iterator it2;
 451    it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
 452    if (it2 != m_freeSurfaces.end())
 453      m_freeSurfaces.erase(it2);
 454    return surf;
 455  }
 456  return VDP_INVALID_HANDLE;
 457}
 458
 459void CVideoSurfaces::Reset()
 460{
 461  CSingleLock lock(m_section);
 462  m_freeSurfaces.clear();
 463  m_state.clear();
 464}
 465
 466int CVideoSurfaces::Size()
 467{
 468  CSingleLock lock(m_section);
 469  return m_state.size();
 470}
 471
 472//-----------------------------------------------------------------------------
 473// CVDPAU
 474//-----------------------------------------------------------------------------
 475
 476CDecoder::CDecoder() : m_vdpauOutput(&m_inMsgEvent)
 477{
 478  m_vdpauConfig.videoSurfaces = &m_videoSurfaces;
 479
 480  m_vdpauConfigured = false;
 481  m_hwContext.bitstream_buffers_allocated = 0;
 482  m_DisplayState = VDPAU_OPEN;
 483  m_vdpauConfig.context = 0;
 484}
 485
 486bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces)
 487{
 488  if(avctx->coded_width  == 0
 489  || avctx->coded_height == 0)
 490  {
 491    CLog::Log(LOGWARNING,"(VDPAU) no width/height available, can't init");
 492    return false;
 493  }
 494  m_vdpauConfig.numRenderBuffers = surfaces;
 495  m_decoderThread = CThread::GetCurrentThreadId();
 496
 497  if ((avctx->codec_id == AV_CODEC_ID_MPEG4) && !g_advancedSettings.m_videoAllowMpeg4VDPAU)
 498    return false;
 499
 500  if (!CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
 501    return false;
 502
 503  m_DisplayState = VDPAU_OPEN;
 504  m_vdpauConfigured = false;
 505
 506  if (!m_dllAvUtil.Load())
 507    return false;
 508
 509  m_presentPicture = 0;
 510
 511  {
 512    VdpDecoderProfile profile = 0;
 513    if(avctx->codec_id == AV_CODEC_ID_H264)
 514      profile = VDP_DECODER_PROFILE_H264_HIGH;
 515#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
 516    else if(avctx->codec_id == AV_CODEC_ID_MPEG4)
 517      profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
 518#endif
 519    if(profile)
 520    {
 521      if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
 522        CLog::Log(LOGWARNING,"(VDPAU) width %i might not be supported because of hardware bug", avctx->width);
 523   
 524      /* attempt to create a decoder with this width/height, some sizes are not supported by hw */
 525      VdpStatus vdp_st;
 526      vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(), profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder);
 527
 528      if(vdp_st != VDP_STATUS_OK)
 529      {
 530        CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) checking for decoder support\n", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
 531        return false;
 532      }
 533
 534      m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
 535      CheckStatus(vdp_st, __LINE__);
 536    }
 537
 538    /* finally setup ffmpeg */
 539    memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
 540    m_hwContext.render = CDecoder::Render;
 541    m_hwContext.bitstream_buffers_allocated = 0;
 542    avctx->get_buffer      = CDecoder::FFGetBuffer;
 543    avctx->reget_buffer    = CDecoder::FFGetBuffer;
 544    avctx->release_buffer  = CDecoder::FFReleaseBuffer;
 545    avctx->draw_horiz_band = CDecoder::FFDrawSlice;
 546    avctx->slice_flags=SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
 547    avctx->hwaccel_context = &m_hwContext;
 548    avctx->thread_count    = 1;
 549
 550    g_Windowing.Register(this);
 551    return true;
 552  }
 553  return false;
 554}
 555
 556CDecoder::~CDecoder()
 557{
 558  Close();
 559}
 560
 561void CDecoder::Close()
 562{
 563  CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
 564
 565  g_Windowing.Unregister(this);
 566
 567  CSingleLock lock(m_DecoderSection);
 568
 569  FiniVDPAUOutput();
 570  m_vdpauOutput.Dispose();
 571
 572  if (m_hwContext.bitstream_buffers_allocated)
 573  {
 574    m_dllAvUtil.av_freep(&m_hwContext.bitstream_buffers);
 575  }
 576
 577  m_dllAvUtil.Unload();
 578
 579  if (m_vdpauConfig.context)
 580    m_vdpauConfig.context->Release();
 581  m_vdpauConfig.context = 0;
 582}
 583
 584long CDecoder::Release()
 585{
 586  // check if we should do some pre-cleanup here
 587  // a second decoder might need resources
 588  if (m_vdpauConfigured == true)
 589  {
 590    CSingleLock lock(m_DecoderSection);
 591    CLog::Log(LOGNOTICE,"CVDPAU::Release pre-cleanup");
 592
 593    Message *reply;
 594    if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
 595                                                   &reply,
 596                                                   2000))
 597    {
 598      bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
 599      reply->Release();
 600      if (!success)
 601      {
 602        CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup returned error", __FUNCTION__);
 603        m_DisplayState = VDPAU_ERROR;
 604      }
 605    }
 606    else
 607    {
 608      CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup timed out", __FUNCTION__);
 609      m_DisplayState = VDPAU_ERROR;
 610    }
 611
 612    VdpVideoSurface surf;
 613    while((surf = m_videoSurfaces.RemoveNext(true)) != VDP_INVALID_HANDLE)
 614    {
 615      m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
 616    }
 617  }
 618  return IHardwareDecoder::Release();
 619}
 620
 621long CDecoder::ReleasePicReference()
 622{
 623  return IHardwareDecoder::Release();
 624}
 625
 626void CDecoder::SetWidthHeight(int width, int height)
 627{
 628  m_vdpauConfig.upscale = g_advancedSettings.m_videoVDPAUScaling;
 629
 630  //pick the smallest dimensions, so we downscale with vdpau and upscale with opengl when appropriate
 631  //this requires the least amount of gpu memory bandwidth
 632  if (g_graphicsContext.GetWidth() < width || g_graphicsContext.GetHeight() < height || m_vdpauConfig.upscale >= 0)
 633  {
 634    //scale width to desktop size if the aspect ratio is the same or bigger than the desktop
 635    if ((double)height * g_graphicsContext.GetWidth() / width <= (double)g_graphicsContext.GetHeight())
 636    {
 637      m_vdpauConfig.outWidth = g_graphicsContext.GetWidth();
 638      m_vdpauConfig.outHeight = MathUtils::round_int((double)height * g_graphicsContext.GetWidth() / width);
 639    }
 640    else //scale height to the desktop size if the aspect ratio is smaller than the desktop
 641    {
 642      m_vdpauConfig.outHeight = g_graphicsContext.GetHeight();
 643      m_vdpauConfig.outWidth = MathUtils::round_int((double)width * g_graphicsContext.GetHeight() / height);
 644    }
 645  }
 646  else
 647  { //let opengl scale
 648    m_vdpauConfig.outWidth = width;
 649    m_vdpauConfig.outHeight = height;
 650  }
 651  CLog::Log(LOGDEBUG, "CVDPAU::SetWidthHeight Setting OutWidth: %i OutHeight: %i", m_vdpauConfig.outWidth, m_vdpauConfig.outHeight);
 652}
 653
 654void CDecoder::OnLostDevice()
 655{
 656  CLog::Log(LOGNOTICE,"CVDPAU::OnLostDevice event");
 657
 658  int count = g_graphicsContext.exit();
 659
 660  CSingleLock lock(m_DecoderSection);
 661  FiniVDPAUOutput();
 662  if (m_vdpauConfig.context)
 663    m_vdpauConfig.context->Release();
 664  m_vdpauConfig.context = 0;
 665
 666  m_DisplayState = VDPAU_LOST;
 667  lock.Leave();
 668  m_DisplayEvent.Reset();
 669
 670  g_graphicsContext.restore(count);
 671}
 672
 673void CDecoder::OnResetDevice()
 674{
 675  CLog::Log(LOGNOTICE,"CVDPAU::OnResetDevice event");
 676
 677  int count = g_graphicsContext.exit();
 678
 679  CSingleLock lock(m_DecoderSection);
 680  if (m_DisplayState == VDPAU_LOST)
 681  {
 682    m_DisplayState = VDPAU_RESET;
 683    lock.Leave();
 684    m_DisplayEvent.Set();
 685  }
 686
 687  g_graphicsContext.restore(count);
 688}
 689
 690int CDecoder::Check(AVCodecContext* avctx)
 691{
 692  EDisplayState state;
 693
 694  { CSingleLock lock(m_DecoderSection);
 695    state = m_DisplayState;
 696  }
 697
 698  if (state == VDPAU_LOST)
 699  {
 700    CLog::Log(LOGNOTICE,"CVDPAU::Check waiting for display reset event");
 701    if (!m_DisplayEvent.WaitMSec(4000))
 702    {
 703      CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
 704      state = VDPAU_RESET;
 705    }
 706    else
 707    {
 708      CSingleLock lock(m_DecoderSection);
 709      state = m_DisplayState;
 710    }
 711  }
 712  if (state == VDPAU_RESET || state == VDPAU_ERROR)
 713  {
 714    CSingleLock lock(m_DecoderSection);
 715
 716    FiniVDPAUOutput();
 717    if (m_vdpauConfig.context)
 718      m_vdpauConfig.context->Release();
 719    m_vdpauConfig.context = 0;
 720
 721    if (CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
 722    {
 723      m_DisplayState = VDPAU_OPEN;
 724      m_vdpauConfigured = false;
 725    }
 726
 727    if (state == VDPAU_RESET)
 728      return VC_FLUSHED;
 729    else
 730      return VC_ERROR;
 731  }
 732  return 0;
 733}
 734
 735bool CDecoder::IsVDPAUFormat(PixelFormat format)
 736{
 737  if (format == AV_PIX_FMT_VDPAU)
 738    return true;
 739  else
 740    return false;
 741}
 742
 743bool CDecoder::Supports(VdpVideoMixerFeature feature)
 744{
 745  return m_vdpauConfig.context->Supports(feature);
 746}
 747
 748bool CDecoder::Supports(EINTERLACEMETHOD method)
 749{
 750  if(method == VS_INTERLACEMETHOD_VDPAU_BOB
 751  || method == VS_INTERLACEMETHOD_AUTO)
 752    return true;
 753
 754  if (method == VS_INTERLACEMETHOD_RENDER_BOB)
 755    return true;
 756
 757  if (method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
 758    return false;
 759
 760  for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
 761  {
 762    if(p->method == method)
 763      return Supports(p->feature);
 764  }
 765  return false;
 766}
 767
 768EINTERLACEMETHOD CDecoder::AutoInterlaceMethod()
 769{
 770  return VS_INTERLACEMETHOD_RENDER_BOB;
 771}
 772
 773void CDecoder::FiniVDPAUOutput()
 774{
 775  if (!m_vdpauConfigured)
 776    return;
 777
 778  CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__);
 779
 780  // uninit output
 781  m_vdpauOutput.Dispose();
 782  m_vdpauConfigured = false;
 783
 784  VdpStatus vdp_st;
 785
 786  vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
 787  if (CheckStatus(vdp_st, __LINE__))
 788    return;
 789  m_vdpauConfig.vdpDecoder = VDP_INVALID_HANDLE;
 790
 791  CLog::Log(LOGDEBUG, "CVDPAU::FiniVDPAUOutput destroying %d video surfaces", m_videoSurfaces.Size());
 792
 793  VdpVideoSurface surf;
 794  while((surf = m_videoSurfaces.RemoveNext()) != VDP_INVALID_HANDLE)
 795  {
 796    m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
 797    if (CheckStatus(vdp_st, __LINE__))
 798      return;
 799  }
 800  m_videoSurfaces.Reset();
 801}
 802
 803void CDecoder::ReadFormatOf( AVCodecID codec
 804                           , VdpDecoderProfile &vdp_decoder_profile
 805                           , VdpChromaType     &vdp_chroma_type)
 806{
 807  switch (codec)
 808  {
 809    case AV_CODEC_ID_MPEG1VIDEO:
 810      vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
 811      vdp_chroma_type     = VDP_CHROMA_TYPE_420;
 812      break;
 813    case AV_CODEC_ID_MPEG2VIDEO:
 814      vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
 815      vdp_chroma_type     = VDP_CHROMA_TYPE_420;
 816      break;
 817    case AV_CODEC_ID_H264:
 818      vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
 819      vdp_chroma_type     = VDP_CHROMA_TYPE_420;
 820      break;
 821    case AV_CODEC_ID_WMV3:
 822      vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
 823      vdp_chroma_type     = VDP_CHROMA_TYPE_420;
 824      break;
 825    case AV_CODEC_ID_VC1:
 826      vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
 827      vdp_chroma_type     = VDP_CHROMA_TYPE_420;
 828      break;
 829    case AV_CODEC_ID_MPEG4:
 830      vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
 831      vdp_chroma_type     = VDP_CHROMA_TYPE_420;
 832      break;
 833    default:
 834      vdp_decoder_profile = 0;
 835      vdp_chroma_type     = 0;
 836      break;
 837  }
 838}
 839
 840bool CDecoder::ConfigVDPAU(AVCodecContext* avctx, int ref_frames)
 841{
 842  FiniVDPAUOutput();
 843
 844  VdpStatus vdp_st;
 845  VdpDecoderProfile vdp_decoder_profile;
 846
 847  m_vdpauConfig.vidWidth = avctx->width;
 848  m_vdpauConfig.vidHeight = avctx->height;
 849  m_vdpauConfig.surfaceWidth = avctx->coded_width;
 850  m_vdpauConfig.surfaceHeight = avctx->coded_height;
 851
 852  SetWidthHeight(avctx->width,avctx->height);
 853
 854  CLog::Log(LOGNOTICE, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i",m_vdpauConfig.outWidth,m_vdpauConfig.vidWidth,m_vdpauConfig.surfaceWidth);
 855  CLog::Log(LOGNOTICE, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",m_vdpauConfig.outHeight,m_vdpauConfig.vidHeight,m_vdpauConfig.surfaceHeight);
 856
 857  ReadFormatOf(avctx->codec_id, vdp_decoder_profile, m_vdpauConfig.vdpChromaType);
 858
 859  if(avctx->codec_id == AV_CODEC_ID_H264)
 860  {
 861    m_vdpauConfig.maxReferences = ref_frames;
 862    if (m_vdpauConfig.maxReferences > 16) m_vdpauConfig.maxReferences = 16;
 863    if (m_vdpauConfig.maxReferences < 5)  m_vdpauConfig.maxReferences = 5;
 864  }
 865  else
 866    m_vdpauConfig.maxReferences = 2;
 867
 868  vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(),
 869                              vdp_decoder_profile,
 870                              m_vdpauConfig.surfaceWidth,
 871                              m_vdpauConfig.surfaceHeight,
 872                              m_vdpauConfig.maxReferences,
 873                              &m_vdpauConfig.vdpDecoder);
 874  if (CheckStatus(vdp_st, __LINE__))
 875    return false;
 876
 877  // initialize output
 878  CSingleLock lock(g_graphicsContext);
 879  m_vdpauConfig.stats = &m_bufferStats;
 880  m_vdpauConfig.vdpau = this;
 881  m_bufferStats.Reset();
 882  m_vdpauOutput.Start();
 883  Message *reply;
 884  if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT,
 885                                                 &reply,
 886                                                 2000,
 887                                                 &m_vdpauConfig,
 888                                                 sizeof(m_vdpauConfig)))
 889  {
 890    bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
 891    reply->Release();
 892    if (!success)
 893    {
 894      CLog::Log(LOGERROR, "VDPAU::%s - vdpau output returned error", __FUNCTION__);
 895      m_vdpauOutput.Dispose();
 896      return false;
 897    }
 898  }
 899  else
 900  {
 901    CLog::Log(LOGERROR, "VDPAU::%s - failed to init output", __FUNCTION__);
 902    m_vdpauOutput.Dispose();
 903    return false;
 904  }
 905
 906  m_inMsgEvent.Reset();
 907  m_vdpauConfigured = true;
 908  return true;
 909}
 910
 911int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic)
 912{
 913  //CLog::Log(LOGNOTICE,"%s",__FUNCTION__);
 914  CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
 915  CDecoder*             vdp        = (CDecoder*)ctx->GetHardware();
 916
 917  // while we are waiting to recover we can't do anything
 918  CSingleLock lock(vdp->m_DecoderSection);
 919
 920  if(vdp->m_DisplayState != VDPAU_OPEN)
 921  {
 922    CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
 923    return -1;
 924  }
 925
 926  VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
 927  surf = vdp->m_videoSurfaces.GetFree(surf != 0 ? surf : VDP_INVALID_HANDLE);
 928
 929  VdpStatus vdp_st = VDP_STATUS_ERROR;
 930  if (surf == VDP_INVALID_HANDLE)
 931  {
 932    // create a new surface
 933    VdpDecoderProfile profile;
 934    ReadFormatOf(avctx->codec_id, profile, vdp->m_vdpauConfig.vdpChromaType);
 935
 936    vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_video_surface_create(vdp->m_vdpauConfig.context->GetDevice(),
 937                                         vdp->m_vdpauConfig.vdpChromaType,
 938                                         avctx->coded_width,
 939                                         avctx->coded_height,
 940                                         &surf);
 941    vdp->CheckStatus(vdp_st, __LINE__);
 942    if (vdp_st != VDP_STATUS_OK)
 943    {
 944      CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
 945      return -1;
 946    }
 947    vdp->m_videoSurfaces.AddSurface(surf);
 948  }
 949
 950  pic->data[1] = pic->data[2] = NULL;
 951  pic->data[0] = (uint8_t*)(uintptr_t)surf;
 952  pic->data[3] = (uint8_t*)(uintptr_t)surf;
 953
 954  pic->linesize[0] = pic->linesize[1] =  pic->linesize[2] = 0;
 955
 956  pic->type= FF_BUFFER_TYPE_USER;
 957
 958  pic->reordered_opaque= avctx->reordered_opaque;
 959  return 0;
 960}
 961
 962void CDecoder::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic)
 963{
 964  CDVDVideoCodecFFmpeg* ctx        = (CDVDVideoCodecFFmpeg*)avctx->opaque;
 965  CDecoder*             vdp        = (CDecoder*)ctx->GetHardware();
 966
 967  VdpVideoSurface surf;
 968  unsigned int i;
 969
 970  CSingleLock lock(vdp->m_DecoderSection);
 971
 972  surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
 973
 974  vdp->m_videoSurfaces.ClearReference(surf);
 975
 976  for(i=0; i<4; i++)
 977    pic->data[i]= NULL;
 978}
 979
 980VdpStatus CDecoder::Render( VdpDecoder decoder, VdpVideoSurface target,
 981                            VdpPictureInfo const *picture_info,
 982                            uint32_t bitstream_buffer_count,
 983                            VdpBitstreamBuffer const * bitstream_buffers)
 984{
 985  return VDP_STATUS_OK;
 986}
 987
 988void CDecoder::FFDrawSlice(struct AVCodecContext *s,
 989                                           const AVFrame *src, int offset[4],
 990                                           int y, int type, int height)
 991{
 992  CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)s->opaque;
 993  CDecoder*               vdp = (CDecoder*)ctx->GetHardware();
 994
 995  // while we are waiting to recover we can't do anything
 996  CSingleLock lock(vdp->m_DecoderSection);
 997
 998  if(vdp->m_DisplayState != VDPAU_OPEN)
 999    return;
1000
1001  if(src->linesize[0] || src->linesize[1] || src->linesize[2]
1002  || offset[0] || offset[1] || offset[2])
1003  {
1004    CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
1005    return;
1006  }
1007
1008  VdpStatus vdp_st;
1009  VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)src->data[3];
1010
1011  // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1012  if (!vdp->m_videoSurfaces.IsValid(surf))
1013  {
1014    CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
1015    return;
1016  }
1017
1018  uint32_t max_refs = 0;
1019  if(s->codec_id == AV_CODEC_ID_H264)
1020    max_refs = vdp->m_hwContext.info.h264.num_ref_frames;
1021
1022  if(vdp->m_vdpauConfig.vdpDecoder == VDP_INVALID_HANDLE
1023  || vdp->m_vdpauConfigured == false
1024  || vdp->m_vdpauConfig.maxReferences < max_refs)
1025  {
1026    if(!vdp->ConfigVDPAU(s, max_refs))
1027      return;
1028  }
1029
1030  uint64_t startTime = CurrentHostCounter();
1031  uint16_t decoded, processed, rend;
1032  vdp->m_bufferStats.Get(decoded, processed, rend);
1033  vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_decoder_render(vdp->m_vdpauConfig.vdpDecoder,
1034                                   surf,
1035                                   (VdpPictureInfo const *)&(vdp->m_hwContext.info),
1036                                   vdp->m_hwContext.bitstream_buffers_used,
1037                                   vdp->m_hwContext.bitstream_buffers);
1038  vdp->CheckStatus(vdp_st, __LINE__);
1039  uint64_t diff = CurrentHostCounter() - startTime;
1040  if (diff*1000/CurrentHostFrequency() > 30)
1041    CLog::Log(LOGDEBUG, "CVDPAU::DrawSlice - VdpDecoderRender long decoding: %d ms, dec: %d, proc: %d, rend: %d", (int)((diff*1000)/CurrentHostFrequency()), decoded, processed, rend);
1042}
1043
1044
1045int CDecoder::Decode(AVCodecContext *avctx, AVFrame *pFrame)
1046{
1047  int result = Check(avctx);
1048  if (result)
1049    return result;
1050
1051  CSingleLock lock(m_DecoderSection);
1052
1053  if (!m_vdpauConfigured)
1054    return VC_ERROR;
1055
1056  if(pFrame)
1057  { // we have a new frame from decoder
1058
1059    VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pFrame->data[3];
1060    // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1061    if (!m_videoSurfaces.IsValid(surf))
1062    {
1063      CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
1064      return VC_BUFFER;
1065    }
1066    m_videoSurfaces.MarkRender(surf);
1067
1068    // send frame to output for processing
1069    CVdpauDecodedPicture pic;
1070    memset(&pic.DVDPic, 0, sizeof(pic.DVDPic));
1071    ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&pic.DVDPic);
1072    pic.videoSurface = surf;
1073    pic.DVDPic.color_matrix = avctx->colorspace;
1074    m_bufferStats.IncDecoded();
1075    m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, &pic, sizeof(pic));
1076
1077    //TODO
1078    // m_codecControl = pic.DVDPic.iFlags & (DVP_FLAG_DRAIN | DVP_FLAG_NO_POSTPROC);
1079  }
1080
1081  int retval = 0;
1082  uint16_t decoded, processed, render;
1083  Message *msg;
1084  while (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1085  {
1086    if (msg->signal == COutputControlProtocol::ERROR)
1087    {
1088      m_DisplayState = VDPAU_ERROR;
1089      retval |= VC_ERROR;
1090    }
1091    msg->Release();
1092  }
1093
1094  m_bufferStats.Get(decoded, processed, render);
1095
1096  uint64_t startTime = CurrentHostCounter();
1097  while (!retval)
1098  {
1099    // first fill the buffers to keep vdpau busy
1100    // mixer will run with decoded >= 2. output is limited by number of output surfaces
1101    // In case mixer is bypassed we limit by looking at processed
1102    if (decoded < 3 && processed < 3)
1103    {
1104      retval |= VC_BUFFER;
1105    }
1106    else if (m_vdpauOutput.m_dataPort.ReceiveInMessage(&msg))
1107    {
1108      if (msg->signal == COutputDataProtocol::PICTURE)
1109      {
1110        if (m_presentPicture)
1111        {
1112          m_presentPicture->ReturnUnused();
1113          m_presentPicture = 0;
1114        }
1115
1116        m_presentPicture = *(CVdpauRenderPicture**)msg->data;
1117        m_presentPicture->vdpau = this;
1118        m_bufferStats.DecRender();
1119        m_bufferStats.Get(decoded, processed, render);
1120        retval |= VC_PICTURE;
1121        msg->Release();
1122        break;
1123      }
1124      msg->Release();
1125    }
1126    else if (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1127    {
1128      if (msg->signal == COutputControlProtocol::STATS)
1129      {
1130        m_bufferStats.Get(decoded, processed, render);
1131      }
1132      else
1133      {
1134        m_DisplayState = VDPAU_ERROR;
1135        retval |= VC_ERROR;
1136      }
1137      msg->Release();
1138    }
1139
1140    if (decoded < 3 && processed < 3)
1141    {
1142      retval |= VC_BUFFER;
1143    }
1144
1145    if (!retval && !m_inMsgEvent.WaitMSec(2000))
1146      break;
1147  }
1148  uint64_t diff = CurrentHostCounter() - startTime;
1149  if (retval & VC_PICTURE)
1150  {
1151    m_bufferStats.SetParams(diff, m_codecControl);
1152  }
1153  if (diff*1000/CurrentHostFrequency() > 50)
1154    CLog::Log(LOGDEBUG,"CVDPAU::Decode long wait: %d", (int)((diff*1000)/CurrentHostFrequency()));
1155
1156  if (!retval)
1157  {
1158    CLog::Log(LOGERROR, "VDPAU::%s - timed out waiting for output message", __FUNCTION__);
1159    m_DisplayState = VDPAU_ERROR;
1160    retval |= VC_ERROR;
1161  }
1162
1163  return retval;
1164}
1165
1166bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
1167{
1168  CSingleLock lock(m_DecoderSection);
1169
1170  if (m_DisplayState != VDPAU_OPEN)
1171    return false;
1172
1173  *picture = m_presentPicture->DVDPic;
1174  picture->vdpau = m_presentPicture;
1175
1176  return true;
1177}
1178
1179void CDecoder::Reset()
1180{
1181  CSingleLock lock(m_DecoderSection);
1182
1183  if (!m_vdpauConfigured)
1184    return;
1185
1186  Message *reply;
1187  if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
1188                                                 &reply,
1189                                                 2000))
1190  {
1191    bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1192    reply->Release();
1193    if (!success)
1194    {
1195      CLog::Log(LOGERROR, "VDPAU::%s - flush returned error", __FUNCTION__);
1196      m_DisplayState = VDPAU_ERROR;
1197    }
1198    else
1199      m_bufferStats.Reset();
1200  }
1201  else
1202  {
1203    CLog::Log(LOGERROR, "VDPAU::%s - flush timed out", __FUNCTION__);
1204    m_DisplayState = VDPAU_ERROR;
1205  }
1206}
1207
1208bool CDecoder::CanSkipDeint()
1209{
1210  return m_bufferStats.CanSkipDeint();
1211}
1212
1213void CDecoder::ReturnRenderPicture(CVdpauRenderPicture *renderPic)
1214{
1215  m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
1216}
1217
1218bool CDecoder::CheckStatus(VdpStatus vdp_st, int line)
1219{
1220  if (vdp_st != VDP_STATUS_OK)
1221  {
1222    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);
1223
1224    if(m_DisplayState == VDPAU_OPEN)
1225    {
1226      if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
1227      {
1228        m_DisplayEvent.Reset();
1229        m_DisplayState = VDPAU_LOST;
1230      }
1231      else
1232        m_DisplayState = VDPAU_ERROR;
1233    }
1234
1235    return true;
1236  }
1237  return false;
1238}
1239
1240//-----------------------------------------------------------------------------
1241// RenderPicture
1242//-----------------------------------------------------------------------------
1243
1244CVdpauRenderPicture* CVdpauRenderPicture::Acquire()
1245{
1246  CSingleLock lock(renderPicSection);
1247
1248  if (refCount == 0)
1249    vdpau->Acquire();
1250
1251  refCount++;
1252  return this;
1253}
1254
1255long CVdpauRenderPicture::Release()
1256{
1257  CSingleLock lock(renderPicSection);
1258
1259  refCount--;
1260  if (refCount > 0)
1261    return refCount;
1262
1263  lock.Leave();
1264  vdpau->ReturnRenderPicture(this);
1265  vdpau->ReleasePicReference();
1266
1267  return refCount;
1268}
1269
1270void CVdpauRenderPicture::ReturnUnused()
1271{
1272  { CSingleLock lock(renderPicSection);
1273    if (refCount > 0)
1274      return;
1275  }
1276  if (vdpau)
1277    vdpau->ReturnRenderPicture(this);
1278}
1279
1280void CVdpauRenderPicture::Sync()
1281{
1282#ifdef GL_ARB_sync
1283  CSingleLock lock(renderPicSection);
1284  if (usefence)
1285  {
1286    if(glIsSync(fence))
1287    {
1288      glDeleteSync(fence);
1289      fence = None;
1290    }
1291    fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1292  }
1293#endif
1294}
1295
1296//-----------------------------------------------------------------------------
1297// Mixer
1298//-----------------------------------------------------------------------------
1299CMixer::CMixer(CEvent *inMsgEvent) :
1300  CThread("Vdpau Mixer"),
1301  m_controlPort("ControlPort", inMsgEvent, &m_outMsgEvent),
1302  m_dataPort("DataPort", inMsgEvent, &m_outMsgEvent)
1303{
1304  m_inMsgEvent = inMsgEvent;
1305}
1306
1307CMixer::~CMixer()
1308{
1309  Dispose();
1310}
1311
1312void CMixer::Start()
1313{
1314  Create();
1315}
1316
1317void CMixer::Dispose()
1318{
1319  m_bStop = true;
1320  m_outMsgEvent.Set();
1321  StopThread();
1322
1323  m_controlPort.Purge();
1324  m_dataPort.Purge();
1325}
1326
1327bool CMixer::IsActive()
1328{
1329  return IsRunning();
1330}
1331
1332void CMixer::OnStartup()
1333{
1334  CLog::Log(LOGNOTICE, "CMixer::OnStartup: Output Thread created");
1335}
1336
1337void CMixer::OnExit()
1338{
1339  CLog::Log(LOGNOTICE, "CMixer::OnExit: Output Thread terminated");
1340}
1341
1342enum MIXER_STATES
1343{
1344  M_TOP = 0,                      // 0
1345  M_TOP_ERROR,                    // 1
1346  M_TOP_UNCONFIGURED,             // 2
1347  M_TOP_CONFIGURED,               // 3
1348  M_TOP_CONFIGURED_WAIT1,         // 4
1349  M_TOP_CONFIGURED_STEP1,         // 5
1350  M_TOP_CONFIGURED_WAIT2,         // 6
1351  M_TOP_CONFIGURED_STEP2,         // 7
1352};
1353
1354int MIXER_parentStates[] = {
1355    -1,
1356    0, //TOP_ERROR
1357    0, //TOP_UNCONFIGURED
1358    0, //TOP_CONFIGURED
1359    3, //TOP_CONFIGURED_WAIT1
1360    3, //TOP_CONFIGURED_STEP1
1361    3, //TOP_CONFIGURED_WAIT2
1362    3, //TOP_CONFIGURED_STEP2
1363};
1364
1365void CMixer::StateMachine(int signal, Protocol *port, Message *msg)
1366{
1367  for (int state = m_state; ; state = MIXER_parentStates[state])
1368  {
1369    switch (state)
1370    {
1371    case M_TOP: // TOP
1372      if (port == &m_controlPort)
1373      {
1374        switch (signal)
1375        {
1376        case CMixerControlProtocol::FLUSH:
1377          Flush();
1378          msg->Reply(CMixerControlProtocol::ACC);
1379          return;
1380        default:
1381          break;
1382        }
1383      }
1384      {
1385        std::string portName = port == NULL ? "timer" : port->portName;
1386        CLog::Log(LOGWARNING, "CMixer::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
1387      }
1388      return;
1389
1390    case M_TOP_ERROR: // TOP
1391      break;
1392
1393    case M_TOP_UNCONFIGURED:
1394      if (port == &m_controlPort)
1395      {
1396        switch (signal)
1397        {
1398        case CMixerControlProtocol::INIT:
1399          CVdpauConfig *data;
1400          data = (CVdpauConfig*)msg->data;
1401          if (data)
1402          {
1403            m_config = *data;
1404          }
1405          Init();
1406          if (!m_vdpError)
1407          {
1408            m_state = M_TOP_CONFIGURED_WAIT1;
1409            msg->Reply(CMixerControlProtocol::ACC);
1410          }
1411          else
1412          {
1413            msg->Reply(CMixerControlProtocol::ERROR);
1414          }
1415          return;
1416        default:
1417          break;
1418        }
1419      }
1420      break;
1421
1422    case M_TOP_CONFIGURED:
1423      if (port == &m_dataPort)
1424      {
1425        switch (signal)
1426        {
1427        case CMixerDataProtocol::FRAME:
1428          CVdpauDecodedPicture *frame;
1429          frame = (CVdpauDecodedPicture*)msg->data;
1430          if (frame)
1431          {
1432            m_decodedPics.push(*frame);
1433          }
1434          m_extTimeout = 0;
1435          return;
1436        case CMixerDataProtocol::BUFFER:
1437          VdpOutputSurface *surf;
1438          surf = (VdpOutputSurface*)msg->data;
1439          if (surf)
1440          {
1441            m_outputSurfaces.push(*surf);
1442          }
1443          m_extTimeout = 0;
1444          return;
1445        default:
1446          break;
1447        }
1448      }
1449      break;
1450
1451    case M_TOP_CONFIGURED_WAIT1:
1452      if (port == NULL) // timeout
1453      {
1454        switch (signal)
1455        {
1456        case CMixerControlProtocol::TIMEOUT:
1457          if (!m_decodedPics.empty() && !m_outputSurfaces.empty())
1458          {
1459            m_state = M_TOP_CONFIGURED_STEP1;
1460            m_bStateMachineSelfTrigger = true;
1461          }
1462          else
1463          {
1464            m_extTimeout = 100;
1465          }
1466          return;
1467        default:
1468          break;
1469        }
1470      }
1471      break;
1472
1473    case M_TOP_CONFIGURED_STEP1:
1474      if (port == NULL) // timeout
1475      {
1476        switch (signal)
1477        {
1478        case CMixerControlProtocol::TIMEOUT:
1479          m_mixerInput.push_front(m_decodedPics.front());
1480          m_decodedPics.pop();
1481          if (m_mixerInput.size() < 2)
1482          {
1483            m_state = M_TOP_CONFIGURED_WAIT1;
1484            m_extTimeout = 0;
1485            return;
1486          }
1487          InitCycle();
1488          ProcessPicture();
1489          if (m_vdpError)
1490          {
1491            m_state = M_TOP_CONFIGURED_WAIT1;
1492            m_extTimeout = 1000;
1493            return;
1494          }
1495          if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
1496            m_outputSurfaces.pop();
1497          m_config.stats->IncProcessed();
1498          m_config.stats->DecDecoded();
1499          m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1500          if (m_mixersteps > 1)
1501          {
1502            m_state = M_TOP_CONFIGURED_WAIT2;
1503            m_extTimeout = 0;
1504          }
1505          else
1506          {
1507            FiniCycle();
1508            m_state = M_TOP_CONFIGURED_WAIT1;
1509            m_extTimeout = 0;
1510          }
1511          return;
1512        default:
1513          break;
1514        }
1515      }
1516      break;
1517
1518    case M_TOP_CONFIGURED_WAIT2:
1519      if (port == NULL) // timeout
1520      {
1521        switch (signal)
1522        {
1523        case CMixerControlProtocol::TIMEOUT:
1524          if (!m_outputSurfaces.empty())
1525          {
1526            m_state = M_TOP_CONFIGURED_STEP2;
1527            m_bStateMachineSelfTrigger = true;
1528          }
1529          else
1530          {
1531            m_extTimeout = 100;
1532          }
1533          return;
1534        default:
1535          break;
1536        }
1537      }
1538      break;
1539
1540    case M_TOP_CONFIGURED_STEP2:
1541       if (port == NULL) // timeout
1542       {
1543         switch (signal)
1544         {
1545         case CMixerControlProtocol::TIMEOUT:
1546           m_processPicture.outputSurface = m_outputSurfaces.front();
1547           m_mixerstep = 1;
1548           ProcessPicture();
1549           if (m_vdpError)
1550           {
1551             m_state = M_TOP_CONFIGURED_WAIT1;
1552             m_extTimeout = 1000;
1553             return;
1554           }
1555           if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420)
1556             m_outputSurfaces.pop();
1557           m_config.stats->IncProcessed();
1558           m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1559           FiniCycle();
1560           m_state = M_TOP_CONFIGURED_WAIT1;
1561           m_extTimeout = 0;
1562           return;
1563         default:
1564           break;
1565         }
1566       }
1567       break;
1568
1569    default: // we are in no state, should not happen
1570      CLog::Log(LOGERROR, "CMixer::%s - no valid state: %d", __FUNCTION__, m_state);
1571      return;
1572    }
1573  } // for
1574}
1575
1576void CMixer::Process()
1577{
1578  Message *msg = NULL;
1579  Protocol *port = NULL;
1580  bool gotMsg;
1581
1582  m_state = M_TOP_UNCONFIGURED;
1583  m_extTimeout = 1000;
1584  m_bStateMachineSelfTrigger = false;
1585
1586  while (!m_bStop)
1587  {
1588    gotMsg = false;
1589
1590    if (m_bStateMachineSelfTrigger)
1591    {
1592      m_bStateMachineSelfTrigger = false;
1593      // self trigger state machine
1594      StateMachine(msg->signal, port, msg);
1595      if (!m_bStateMachineSelfTrigger)
1596      {
1597        msg->Release();
1598        msg = NULL;
1599      }
1600      continue;
1601    }
1602    // check control port
1603    else if (m_controlPort.ReceiveOutMessage(&msg))
1604    {
1605      gotMsg = true;
1606      port = &m_controlPort;
1607    }
1608    // check data port
1609    else if (m_dataPort.ReceiveOutMessage(&msg))
1610    {
1611      gotMsg = true;
1612      port = &m_dataPort;
1613    }
1614
1615    if (gotMsg)
1616    {
1617      StateMachine(msg->signal, port, msg);
1618      if (!m_bStateMachineSelfTrigger)
1619      {
1620        msg->Release();
1621        msg = NULL;
1622      }
1623      continue;
1624    }
1625
1626    // wait for message
1627    else if (m_outMsgEvent.WaitMSec(m_extTimeout))
1628    {
1629      continue;
1630    }
1631    // time out
1632    else
1633    {
1634      msg = m_controlPort.GetMessage();
1635      msg->signal = CMixerControlProtocol::TIMEOUT;
1636      port = 0;
1637      // signal timeout to state machine
1638      StateMachine(msg->signal, port, msg);
1639      if (!m_bStateMachineSelfTrigger)
1640      {
1641        msg->Release();
1642        msg = NULL;
1643      }
1644    }
1645  }
1646  Uninit();
1647}
1648
1649void CMixer::CreateVdpauMixer()
1650{
1651  CLog::Log(LOGNOTICE, " (VDPAU) Creating the video mixer");
1652
1653  InitCSCMatrix(m_config.vidWidth);
1654
1655  VdpVideoMixerParameter parameters[] = {
1656    VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
1657    VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
1658    VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE};
1659
1660  void const * parameter_values[] = {
1661    &m_config.surfaceWidth,
1662    &m_config.surfaceHeight,
1663    &m_config.vdpChromaType};
1664
1665  VdpStatus vdp_st = VDP_STATUS_ERROR;
1666  vdp_st = m_config.context->GetProcs().vdp_video_mixer_create(m_config.context->GetDevice(),
1667                                m_config.context->GetFeatureCount(),
1668                                m_config.context->GetFeatures(),
1669                                ARSIZE(parameters),
1670                                parameters,
1671                                parameter_values,
1672                                &m_videoMixer);
1673  CheckStatus(vdp_st, __LINE__);
1674
1675  // create 3 pitches of black lines needed for clipping top
1676  // and bottom lines when de-interlacing
1677  m_BlackBar = new uint32_t[3*m_config.outWidth];
1678  memset(m_BlackBar, 0, 3*m_config.outWidth*sizeof(uint32_t));
1679
1680}
1681
1682void CMixer::InitCSCMatrix(int Width)
1683{
1684  m_Procamp.struct_version = VDP_PROCAMP_VERSION;
1685  m_Procamp.brightness     = 0.0;
1686  m_Procamp.contrast       = 1.0;
1687  m_Procamp.saturation     = 1.0;
1688  m_Procamp.hue            = 0;
1689}
1690
1691void CMixer::CheckFeatures()
1692{
1693  if (m_Upscale != m_config.upscale)
1694  {
1695    SetHWUpscaling();
1696    m_Upscale = m_config.upscale;
1697  }
1698  if (m_Brightness != CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness ||
1699      m_Contrast   != CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast ||
1700      m_ColorMatrix != m_mixerInput[1].DVDPic.color_matrix)
1701  {
1702    SetColor();
1703    m_Brightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
1704    m_Contrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
1705    m_ColorMatrix = m_mixerInput[1].DVDPic.color_matrix;
1706  }
1707  if (m_NoiseReduction != CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction)
1708  {
1709    m_NoiseReduction = CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction;
1710    SetNoiseReduction();
1711  }
1712  if (m_Sharpness != CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
1713  {
1714    m_Sharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
1715    SetSharpness();
1716  }
1717  if (m_DeintMode != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode ||
1718      m_Deint     != CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod)
1719  {
1720    m_DeintMode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
1721    m_Deint     = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod;
1722    SetDeinterlacing();
1723  }
1724}
1725
1726void CMixer::SetPostProcFeatures(bool postProcEnabled)
1727{
1728  if (m_PostProc != postProcEnabled)
1729  {
1730    if (postProcEnabled)
1731    {
1732      SetNoiseReduction();
1733      SetSharpness();
1734      SetDeinterlacing();
1735      SetHWUpscaling();
1736    }
1737    else
1738      PostProcOff();
1739    m_PostProc = postProcEnabled;
1740  }
1741}
1742
1743void CMixer::P

Large files files are truncated, but you can click here to view the full file