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

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

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