1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "VDPAU.h"
10
11 #include "Application.h"
12 #include "DVDCodecs/DVDCodecUtils.h"
13 #include "ServiceBroker.h"
14 #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
15 #include "cores/VideoPlayer/Interface/TimingConstants.h"
16 #include "cores/VideoPlayer/Process/ProcessInfo.h"
17 #include "cores/VideoPlayer/VideoRenderers/RenderFlags.h"
18 #include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
19 #include "guilib/TextureManager.h"
20 #include "rendering/RenderSystem.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/Settings.h"
23 #include "settings/SettingsComponent.h"
24 #include "settings/lib/Setting.h"
25 #include "utils/MathUtils.h"
26 #include "utils/TimeUtils.h"
27 #include "utils/log.h"
28 #include "windowing/GraphicContext.h"
29 #include "windowing/X11/WinSystemX11.h"
30
31 #include <dlfcn.h>
32
33 using namespace Actor;
34 using namespace VDPAU;
35 #define NUM_RENDER_PICS 7
36 #define NUM_CROP_PIX 3
37
38 #define ARSIZE(x) (sizeof(x) / sizeof((x)[0]))
39
40 CDecoder::Desc decoder_profiles[] = {
41 {"MPEG1", VDP_DECODER_PROFILE_MPEG1},
42 {"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE},
43 {"MPEG2_MAIN", VDP_DECODER_PROFILE_MPEG2_MAIN},
44 {"H264_BASELINE",VDP_DECODER_PROFILE_H264_BASELINE},
45 {"H264_MAIN", VDP_DECODER_PROFILE_H264_MAIN},
46 {"H264_HIGH", VDP_DECODER_PROFILE_H264_HIGH},
47 {"VC1_SIMPLE", VDP_DECODER_PROFILE_VC1_SIMPLE},
48 {"VC1_MAIN", VDP_DECODER_PROFILE_VC1_MAIN},
49 {"VC1_ADVANCED", VDP_DECODER_PROFILE_VC1_ADVANCED},
50 {"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP},
51 #ifdef VDP_DECODER_PROFILE_HEVC_MAIN
52 {"HEVC_MAIN", VDP_DECODER_PROFILE_HEVC_MAIN},
53 #endif
54 #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_0
55 {"VP9_PROFILE_0", VDP_DECODER_PROFILE_VP9_PROFILE_0},
56 #endif
57 };
58
59 static struct SInterlaceMapping
60 {
61 const EINTERLACEMETHOD method;
62 const VdpVideoMixerFeature feature;
63 } g_interlace_mapping[] =
64 { {VS_INTERLACEMETHOD_VDPAU_TEMPORAL , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
65 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL}
66 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
67 , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
68 , {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE}
69 , {VS_INTERLACEMETHOD_NONE , (VdpVideoMixerFeature)-1}
70 };
71
72 static float studioCSCKCoeffs601[3] = {0.299, 0.587, 0.114}; //BT601 {Kr, Kg, Kb}
73 static float studioCSCKCoeffs709[3] = {0.2126, 0.7152, 0.0722}; //BT709 {Kr, Kg, Kb}
74
75 //-----------------------------------------------------------------------------
76 //-----------------------------------------------------------------------------
77
78 CVDPAUContext *CVDPAUContext::m_context = 0;
79 CCriticalSection CVDPAUContext::m_section;
80 Display *CVDPAUContext::m_display = 0;
81 void *CVDPAUContext::m_dlHandle = 0;
82
CVDPAUContext()83 CVDPAUContext::CVDPAUContext()
84 {
85 m_context = 0;
86 m_refCount = 0;
87 }
88
Release()89 void CVDPAUContext::Release()
90 {
91 CSingleLock lock(m_section);
92
93 m_refCount--;
94 if (m_refCount <= 0)
95 {
96 Close();
97 delete this;
98 m_context = 0;
99 }
100 }
101
Close()102 void CVDPAUContext::Close()
103 {
104 CLog::Log(LOGINFO, "VDPAU::Close - closing decoder context");
105 DestroyContext();
106 }
107
EnsureContext(CVDPAUContext ** ctx)108 bool CVDPAUContext::EnsureContext(CVDPAUContext **ctx)
109 {
110 CSingleLock lock(m_section);
111
112 if (m_context)
113 {
114 m_context->m_refCount++;
115 *ctx = m_context;
116 return true;
117 }
118
119 m_context = new CVDPAUContext();
120 *ctx = m_context;
121 {
122 CSingleLock gLock(CServiceBroker::GetWinSystem()->GetGfxContext());
123 if (!m_context->LoadSymbols() || !m_context->CreateContext())
124 {
125 delete m_context;
126 m_context = 0;
127 *ctx = NULL;
128 return false;
129 }
130 }
131
132 m_context->m_refCount++;
133
134 *ctx = m_context;
135 return true;
136 }
137
GetProcs()138 VDPAU_procs& CVDPAUContext::GetProcs()
139 {
140 return m_vdpProcs;
141 }
142
GetFeatures()143 VdpVideoMixerFeature* CVDPAUContext::GetFeatures()
144 {
145 return m_vdpFeatures;
146 }
147
GetFeatureCount()148 int CVDPAUContext::GetFeatureCount()
149 {
150 return m_featureCount;
151 }
152
LoadSymbols()153 bool CVDPAUContext::LoadSymbols()
154 {
155 if (!m_dlHandle)
156 {
157 m_dlHandle = dlopen("libvdpau.so.1", RTLD_LAZY);
158 if (!m_dlHandle)
159 {
160 const char* error = dlerror();
161 if (!error)
162 error = "dlerror() returned NULL";
163
164 CLog::Log(LOGERROR,"VDPAU::LoadSymbols: Unable to get handle to lib: %s", error);
165 return false;
166 }
167 }
168
169 char* error;
170 (void)dlerror();
171 dl_vdp_device_create_x11 = (VdpStatus (*)(Display*, int, VdpDevice*, VdpStatus (**)(VdpDevice, VdpFuncId, void**)))dlsym(m_dlHandle, (const char*)"vdp_device_create_x11");
172 error = dlerror();
173 if (error)
174 {
175 CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__);
176 m_vdpDevice = VDP_INVALID_HANDLE;
177 return false;
178 }
179 return true;
180 }
181
CreateContext()182 bool CVDPAUContext::CreateContext()
183 {
184 CLog::Log(LOGINFO, "VDPAU::CreateContext - creating decoder context");
185
186 int screen;
187 { CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
188
189 if (!m_display)
190 m_display = XOpenDisplay(NULL);
191
192 if (!m_display)
193 return false;
194
195 screen = static_cast<KODI::WINDOWING::X11::CWinSystemX11*>(CServiceBroker::GetWinSystem())->GetScreen();
196 }
197
198 VdpStatus vdp_st;
199 // Create Device
200 vdp_st = dl_vdp_device_create_x11(m_display,
201 screen,
202 &m_vdpDevice,
203 &m_vdpProcs.vdp_get_proc_address);
204
205 CLog::Log(LOGINFO, "vdp_device = 0x%08x vdp_st = 0x%08x", m_vdpDevice, vdp_st);
206 if (vdp_st != VDP_STATUS_OK)
207 {
208 CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x. Falling back.",vdp_st);
209 m_vdpDevice = VDP_INVALID_HANDLE;
210 return false;
211 }
212
213 QueryProcs();
214 SpewHardwareAvailable();
215 return true;
216 }
217
QueryProcs()218 void CVDPAUContext::QueryProcs()
219 {
220 VdpStatus vdp_st;
221
222 #define VDP_PROC(id, proc) \
223 do { \
224 vdp_st = m_vdpProcs.vdp_get_proc_address(m_vdpDevice, id, (void**)&proc); \
225 if (vdp_st != VDP_STATUS_OK) \
226 { \
227 CLog::Log(LOGERROR, "CVDPAUContext::GetProcs - failed to get proc id"); \
228 } \
229 } while(0);
230
231 VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING , m_vdpProcs.vdp_get_error_string);
232 VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY , m_vdpProcs.vdp_device_destroy);
233 VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX , m_vdpProcs.vdp_generate_csc_matrix);
234 VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE , m_vdpProcs.vdp_video_surface_create);
235 VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY , m_vdpProcs.vdp_video_surface_destroy);
236 VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR , m_vdpProcs.vdp_video_surface_put_bits_y_cb_cr);
237 VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR , m_vdpProcs.vdp_video_surface_get_bits_y_cb_cr);
238 VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR , m_vdpProcs.vdp_output_surface_put_bits_y_cb_cr);
239 VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE , m_vdpProcs.vdp_output_surface_put_bits_native);
240 VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE , m_vdpProcs.vdp_output_surface_create);
241 VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY , m_vdpProcs.vdp_output_surface_destroy);
242 VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE , m_vdpProcs.vdp_output_surface_get_bits_native);
243 VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpProcs.vdp_output_surface_render_output_surface);
244 VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED , m_vdpProcs.vdp_output_surface_put_bits_indexed);
245 VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE , m_vdpProcs.vdp_video_mixer_create);
246 VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES , m_vdpProcs.vdp_video_mixer_set_feature_enables);
247 VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY , m_vdpProcs.vdp_video_mixer_destroy);
248 VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER , m_vdpProcs.vdp_video_mixer_render);
249 VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES , m_vdpProcs.vdp_video_mixer_set_attribute_values);
250 VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpProcs.vdp_video_mixer_query_parameter_support);
251 VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT , m_vdpProcs.vdp_video_mixer_query_feature_support);
252 VDP_PROC(VDP_FUNC_ID_DECODER_CREATE , m_vdpProcs.vdp_decoder_create);
253 VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY , m_vdpProcs.vdp_decoder_destroy);
254 VDP_PROC(VDP_FUNC_ID_DECODER_RENDER , m_vdpProcs.vdp_decoder_render);
255 VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES , m_vdpProcs.vdp_decoder_query_caps);
256 #undef VDP_PROC
257 }
258
GetDevice()259 VdpDevice CVDPAUContext::GetDevice()
260 {
261 return m_vdpDevice;
262 }
263
DestroyContext()264 void CVDPAUContext::DestroyContext()
265 {
266 if (!m_vdpProcs.vdp_device_destroy)
267 return;
268
269 m_vdpProcs.vdp_device_destroy(m_vdpDevice);
270 m_vdpDevice = VDP_INVALID_HANDLE;
271 }
272
SpewHardwareAvailable()273 void CVDPAUContext::SpewHardwareAvailable() //Copyright (c) 2008 Wladimir J. van der Laan -- VDPInfo
274 {
275 VdpStatus rv;
276 CLog::Log(LOGINFO, "VDPAU Decoder capabilities:");
277 CLog::Log(LOGINFO, "name level macbs width height");
278 CLog::Log(LOGINFO, "------------------------------------");
279 for(const CDecoder::Desc& decoder_profile : decoder_profiles)
280 {
281 VdpBool is_supported = false;
282 uint32_t max_level, max_macroblocks, max_width, max_height;
283 rv = m_vdpProcs.vdp_decoder_query_caps(m_vdpDevice, decoder_profile.id,
284 &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
285 if(rv == VDP_STATUS_OK && is_supported)
286 {
287 CLog::Log(LOGINFO, "%-16s %2i %5i %5i %5i", decoder_profile.name, max_level, max_macroblocks,
288 max_width, max_height);
289 }
290 }
291 CLog::Log(LOGINFO, "------------------------------------");
292 m_featureCount = 0;
293 #define CHECK_SUPPORT(feature) \
294 do \
295 { \
296 VdpBool supported; \
297 if (m_vdpProcs.vdp_video_mixer_query_feature_support(m_vdpDevice, feature, &supported) == \
298 VDP_STATUS_OK && \
299 supported) \
300 { \
301 CLog::Log(LOGINFO, "Mixer feature: " #feature); \
302 m_vdpFeatures[m_featureCount++] = feature; \
303 } \
304 } while (false)
305
306 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION);
307 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_SHARPNESS);
308 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL);
309 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL);
310 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE);
311 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
312 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1);
313 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2);
314 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3);
315 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4);
316 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5);
317 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6);
318 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7);
319 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8);
320 CHECK_SUPPORT(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9);
321 #endif
322 #undef CHECK_SUPPORT
323 }
324
Supports(VdpVideoMixerFeature feature)325 bool CVDPAUContext::Supports(VdpVideoMixerFeature feature)
326 {
327 for(int i = 0; i < m_featureCount; i++)
328 {
329 if(m_vdpFeatures[i] == feature)
330 return true;
331 }
332 return false;
333 }
334
335 //-----------------------------------------------------------------------------
336 // VDPAU Video Surface states
337 //-----------------------------------------------------------------------------
338
339 #define SURFACE_USED_FOR_REFERENCE 0x01
340 #define SURFACE_USED_FOR_RENDER 0x02
341
AddSurface(VdpVideoSurface surf)342 void CVideoSurfaces::AddSurface(VdpVideoSurface surf)
343 {
344 CSingleLock lock(m_section);
345 m_state[surf] = SURFACE_USED_FOR_REFERENCE;
346 }
347
ClearReference(VdpVideoSurface surf)348 void CVideoSurfaces::ClearReference(VdpVideoSurface surf)
349 {
350 CSingleLock lock(m_section);
351 if (m_state.find(surf) == m_state.end())
352 {
353 CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
354 return;
355 }
356 m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
357 if (m_state[surf] == 0)
358 {
359 m_freeSurfaces.push_back(surf);
360 }
361 }
362
MarkRender(VdpVideoSurface surf)363 bool CVideoSurfaces::MarkRender(VdpVideoSurface surf)
364 {
365 CSingleLock lock(m_section);
366 if (m_state.find(surf) == m_state.end())
367 {
368 CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
369 return false;
370 }
371 std::list<VdpVideoSurface>::iterator it;
372 it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
373 if (it != m_freeSurfaces.end())
374 {
375 m_freeSurfaces.erase(it);
376 }
377 m_state[surf] |= SURFACE_USED_FOR_RENDER;
378 return true;
379 }
380
ClearRender(VdpVideoSurface surf)381 void CVideoSurfaces::ClearRender(VdpVideoSurface surf)
382 {
383 CSingleLock lock(m_section);
384 if (m_state.find(surf) == m_state.end())
385 {
386 CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
387 return;
388 }
389 m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
390 if (m_state[surf] == 0)
391 {
392 m_freeSurfaces.push_back(surf);
393 }
394 }
395
IsValid(VdpVideoSurface surf)396 bool CVideoSurfaces::IsValid(VdpVideoSurface surf)
397 {
398 CSingleLock lock(m_section);
399 if (m_state.find(surf) != m_state.end())
400 return true;
401 else
402 return false;
403 }
404
GetFree(VdpVideoSurface surf)405 VdpVideoSurface CVideoSurfaces::GetFree(VdpVideoSurface surf)
406 {
407 CSingleLock lock(m_section);
408 if (m_state.find(surf) != m_state.end())
409 {
410 std::list<VdpVideoSurface>::iterator it;
411 it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
412 if (it == m_freeSurfaces.end())
413 {
414 CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
415 }
416 else
417 {
418 m_freeSurfaces.erase(it);
419 m_state[surf] = SURFACE_USED_FOR_REFERENCE;
420 return surf;
421 }
422 }
423
424 if (!m_freeSurfaces.empty())
425 {
426 VdpVideoSurface freeSurf = m_freeSurfaces.front();
427 m_freeSurfaces.pop_front();
428 m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
429 return freeSurf;
430 }
431
432 return VDP_INVALID_HANDLE;
433 }
434
RemoveNext(bool skiprender)435 VdpVideoSurface CVideoSurfaces::RemoveNext(bool skiprender)
436 {
437 CSingleLock lock(m_section);
438 VdpVideoSurface surf;
439 std::map<VdpVideoSurface, int>::iterator it;
440 for(it = m_state.begin(); it != m_state.end(); ++it)
441 {
442 if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
443 continue;
444 surf = it->first;
445 m_state.erase(surf);
446
447 std::list<VdpVideoSurface>::iterator it2;
448 it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
449 if (it2 != m_freeSurfaces.end())
450 m_freeSurfaces.erase(it2);
451 return surf;
452 }
453 return VDP_INVALID_HANDLE;
454 }
455
Reset()456 void CVideoSurfaces::Reset()
457 {
458 CSingleLock lock(m_section);
459 m_freeSurfaces.clear();
460 m_state.clear();
461 }
462
Size()463 int CVideoSurfaces::Size()
464 {
465 CSingleLock lock(m_section);
466 return m_state.size();
467 }
468
HasRefs()469 bool CVideoSurfaces::HasRefs()
470 {
471 CSingleLock lock(m_section);
472 for (const auto &i : m_state)
473 {
474 if (i.second & SURFACE_USED_FOR_REFERENCE)
475 return true;
476 }
477 return false;
478 }
479
480 //-----------------------------------------------------------------------------
481 // CVDPAU
482 //-----------------------------------------------------------------------------
483
484 bool CDecoder::m_capGeneral = false;
485
CDecoder(CProcessInfo & processInfo)486 CDecoder::CDecoder(CProcessInfo& processInfo) :
487 m_vdpauOutput(*this, &m_inMsgEvent), m_processInfo(processInfo)
488 {
489 m_vdpauConfig.videoSurfaces = &m_videoSurfaces;
490
491 m_vdpauConfigured = false;
492 m_DisplayState = VDPAU_OPEN;
493 m_vdpauConfig.context = 0;
494 m_vdpauConfig.processInfo = &m_processInfo;
495 m_vdpauConfig.resetCounter = 0;
496 }
497
Open(AVCodecContext * avctx,AVCodecContext * mainctx,const enum AVPixelFormat fmt)498 bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, const enum AVPixelFormat fmt)
499 {
500 // this could be done better by querying actual hw capabilities
501 // but since vdpau will be dropped anyway in v19, this should do
502 if (avctx->sw_pix_fmt != AV_PIX_FMT_YUV420P &&
503 avctx->sw_pix_fmt != AV_PIX_FMT_YUVJ420P)
504 return false;
505
506 // check if user wants to decode this format with VDPAU
507 std::string gpuvendor = CServiceBroker::GetRenderSystem()->GetRenderVendor();
508 std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
509 // nvidia is whitelisted despite for mpeg-4 we need to query user settings
510 if ((gpuvendor.compare(0, 6, "nvidia") != 0) || (avctx->codec_id == AV_CODEC_ID_MPEG4) || (avctx->codec_id == AV_CODEC_ID_H263))
511 {
512 std::map<AVCodecID, std::string> settings_map = {
513 { AV_CODEC_ID_H263, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG4 },
514 { AV_CODEC_ID_MPEG4, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG4 },
515 { AV_CODEC_ID_WMV3, CSettings::SETTING_VIDEOPLAYER_USEVDPAUVC1 },
516 { AV_CODEC_ID_VC1, CSettings::SETTING_VIDEOPLAYER_USEVDPAUVC1 },
517 { AV_CODEC_ID_MPEG2VIDEO, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG2 },
518 };
519
520 auto settingsComponent = CServiceBroker::GetSettingsComponent();
521 if (!settingsComponent)
522 return false;
523
524 auto settings = settingsComponent->GetSettings();
525 if (!settings)
526 return false;
527
528 auto entry = settings_map.find(avctx->codec_id);
529 if (entry != settings_map.end())
530 {
531 auto setting = settings->GetSetting(entry->second);
532 if (!setting)
533 {
534 CLog::Log(LOGERROR, "Failed to load setting for: {}", entry->second);
535 return false;
536 }
537
538 bool enabled = settings->GetBool(entry->second) && setting->IsVisible();
539 if (!enabled)
540 return false;
541 }
542 }
543
544 if (!CServiceBroker::GetRenderSystem()->IsExtSupported("GL_NV_vdpau_interop"))
545 {
546 CLog::Log(LOGINFO, "VDPAU::Open: required extension GL_NV_vdpau_interop not found");
547 return false;
548 }
549
550 if (avctx->coded_width == 0 ||
551 avctx->coded_height == 0)
552 {
553 CLog::Log(LOGWARNING,"VDPAU::Open: no width/height available, can't init");
554 return false;
555 }
556 m_vdpauConfig.numRenderBuffers = 5;
557 m_vdpauConfig.timeOpened = CurrentHostCounter();
558
559 if (!CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
560 return false;
561
562 m_DisplayState = VDPAU_OPEN;
563 m_vdpauConfigured = false;
564
565 m_presentPicture = 0;
566
567 {
568 VdpDecoderProfile profile = 0;
569
570 // convert FFMPEG codec ID to VDPAU profile.
571 ReadFormatOf(avctx->codec_id, profile, m_vdpauConfig.vdpChromaType);
572 if(profile)
573 {
574 VdpStatus vdp_st;
575 VdpBool is_supported = false;
576 uint32_t max_level, max_macroblocks, max_width, max_height;
577
578 // query device capabilities to ensure that VDPAU can handle the requested codec
579 vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_query_caps(m_vdpauConfig.context->GetDevice(),
580 profile, &is_supported, &max_level, &max_macroblocks, &max_width, &max_height);
581
582 // test to make sure there is a possibility the codec will work
583 if (CheckStatus(vdp_st, __LINE__))
584 {
585 CLog::Log(LOGERROR, "VDPAU::Open: error %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
586 return false;
587 }
588
589 if (max_width < (uint32_t) avctx->coded_width || max_height < (uint32_t) avctx->coded_height)
590 {
591 CLog::Log(LOGWARNING,"VDPAU::Open: requested picture dimensions (%i, %i) exceed hardware capabilities ( %i, %i).",
592 avctx->coded_width, avctx->coded_height, max_width, max_height);
593 return false;
594 }
595
596 if (!CDVDCodecUtils::IsVP3CompatibleWidth(avctx->coded_width))
597 CLog::Log(LOGWARNING,"VDPAU::Open width %i might not be supported because of hardware bug", avctx->width);
598
599 // attempt to create a decoder with this width/height, some sizes are not supported by hw
600 vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(), profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder);
601
602 if (CheckStatus(vdp_st, __LINE__))
603 {
604 CLog::Log(LOGERROR, "VDPAU::Open: error: %s(%d) checking for decoder support", m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st);
605 return false;
606 }
607
608 m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
609 CheckStatus(vdp_st, __LINE__);
610
611 // finally setup ffmpeg
612 memset(&m_hwContext, 0, sizeof(AVVDPAUContext));
613 m_hwContext.render2 = CDecoder::Render;
614 avctx->get_buffer2 = CDecoder::FFGetBuffer;
615 avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
616 avctx->hwaccel_context = &m_hwContext;
617
618 CServiceBroker::GetWinSystem()->Register(this);
619 m_avctx = mainctx;
620 return true;
621 }
622 }
623 return false;
624 }
625
~CDecoder()626 CDecoder::~CDecoder()
627 {
628 Close();
629 }
630
Close()631 void CDecoder::Close()
632 {
633 CLog::Log(LOGINFO, " (VDPAU) %s", __FUNCTION__);
634
635 CServiceBroker::GetWinSystem()->Unregister(this);
636
637 CSingleLock lock(m_DecoderSection);
638
639 FiniVDPAUOutput();
640 m_vdpauOutput.Dispose();
641
642 if (m_vdpauConfig.context)
643 m_vdpauConfig.context->Release();
644 m_vdpauConfig.context = 0;
645 }
646
Release()647 long CDecoder::Release()
648 {
649 // if ffmpeg holds any references, flush buffers
650 if (m_avctx && m_videoSurfaces.HasRefs())
651 {
652 avcodec_flush_buffers(m_avctx);
653 }
654
655 // check if we should do some pre-cleanup here
656 // a second decoder might need resources
657 if (m_vdpauConfigured == true)
658 {
659 CSingleLock lock(m_DecoderSection);
660 CLog::Log(LOGINFO, "CVDPAU::Release pre-cleanup");
661
662 Message *reply;
663 if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
664 &reply,
665 2000))
666 {
667 bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
668 reply->Release();
669 if (!success)
670 {
671 CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup returned error", __FUNCTION__);
672 m_DisplayState = VDPAU_ERROR;
673 }
674 }
675 else
676 {
677 CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup timed out", __FUNCTION__);
678 m_DisplayState = VDPAU_ERROR;
679 }
680
681 VdpVideoSurface surf;
682 while((surf = m_videoSurfaces.RemoveNext(true)) != VDP_INVALID_HANDLE)
683 {
684 m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
685 }
686 }
687 return IHardwareDecoder::Release();
688 }
689
ReleasePicReference()690 long CDecoder::ReleasePicReference()
691 {
692 return IHardwareDecoder::Release();
693 }
694
SetWidthHeight(int width,int height)695 void CDecoder::SetWidthHeight(int width, int height)
696 {
697 m_vdpauConfig.upscale = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUScaling;
698
699 //pick the smallest dimensions, so we downscale with vdpau and upscale with opengl when appropriate
700 //this requires the least amount of gpu memory bandwidth
701 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() < width || CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight() < height || m_vdpauConfig.upscale >= 0)
702 {
703 //scale width to desktop size if the aspect ratio is the same or bigger than the desktop
704 if ((double)height * CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() / width <= (double)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())
705 {
706 m_vdpauConfig.outWidth = CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth();
707 m_vdpauConfig.outHeight = MathUtils::round_int((double)height * CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() / width);
708 }
709 else //scale height to the desktop size if the aspect ratio is smaller than the desktop
710 {
711 m_vdpauConfig.outHeight = CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight();
712 m_vdpauConfig.outWidth = MathUtils::round_int((double)width * CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight() / height);
713 }
714 }
715 else
716 { //let opengl scale
717 m_vdpauConfig.outWidth = width;
718 m_vdpauConfig.outHeight = height;
719 }
720 CLog::Log(LOGDEBUG, LOGVIDEO, "CVDPAU::SetWidthHeight Setting OutWidth: %i OutHeight: %i", m_vdpauConfig.outWidth, m_vdpauConfig.outHeight);
721 }
722
OnLostDisplay()723 void CDecoder::OnLostDisplay()
724 {
725 CLog::Log(LOGINFO, "CVDPAU::OnLostDevice event");
726
727 int count = CServiceBroker::GetWinSystem()->GetGfxContext().exit();
728
729 CSingleLock lock(m_DecoderSection);
730 FiniVDPAUOutput();
731 if (m_vdpauConfig.context)
732 m_vdpauConfig.context->Release();
733 m_vdpauConfig.context = 0;
734
735 m_DisplayState = VDPAU_LOST;
736 lock.Leave();
737 m_DisplayEvent.Reset();
738
739 CServiceBroker::GetWinSystem()->GetGfxContext().restore(count);
740 }
741
OnResetDisplay()742 void CDecoder::OnResetDisplay()
743 {
744 CLog::Log(LOGINFO, "CVDPAU::OnResetDevice event");
745
746 int count = CServiceBroker::GetWinSystem()->GetGfxContext().exit();
747
748 CSingleLock lock(m_DecoderSection);
749 if (m_DisplayState == VDPAU_LOST)
750 {
751 m_DisplayState = VDPAU_RESET;
752 lock.Leave();
753 m_DisplayEvent.Set();
754 }
755
756 CServiceBroker::GetWinSystem()->GetGfxContext().restore(count);
757 }
758
Check(AVCodecContext * avctx)759 CDVDVideoCodec::VCReturn CDecoder::Check(AVCodecContext* avctx)
760 {
761 EDisplayState state;
762
763 { CSingleLock lock(m_DecoderSection);
764 state = m_DisplayState;
765 }
766
767 if (state == VDPAU_LOST)
768 {
769 CLog::Log(LOGINFO, "CVDPAU::Check waiting for display reset event");
770 if (!m_DisplayEvent.WaitMSec(4000))
771 {
772 CLog::Log(LOGERROR, "CVDPAU::Check - device didn't reset in reasonable time");
773 state = VDPAU_RESET;
774 }
775 else
776 {
777 CSingleLock lock(m_DecoderSection);
778 state = m_DisplayState;
779 }
780 }
781 if (state == VDPAU_RESET || state == VDPAU_ERROR)
782 {
783 CSingleLock lock(m_DecoderSection);
784
785 avcodec_flush_buffers(avctx);
786 FiniVDPAUOutput();
787 if (m_vdpauConfig.context)
788 m_vdpauConfig.context->Release();
789 m_vdpauConfig.context = 0;
790
791 if (CVDPAUContext::EnsureContext(&m_vdpauConfig.context))
792 {
793 m_DisplayState = VDPAU_OPEN;
794 m_vdpauConfigured = false;
795 }
796
797 if (state == VDPAU_RESET)
798 return CDVDVideoCodec::VC_FLUSHED;
799 else
800 return CDVDVideoCodec::VC_ERROR;
801 }
802 return CDVDVideoCodec::VC_NONE;
803 }
804
IsVDPAUFormat(AVPixelFormat format)805 bool CDecoder::IsVDPAUFormat(AVPixelFormat format)
806 {
807 if (format == AV_PIX_FMT_VDPAU)
808 return true;
809 else
810 return false;
811 }
812
Supports(VdpVideoMixerFeature feature)813 bool CDecoder::Supports(VdpVideoMixerFeature feature)
814 {
815 return m_vdpauConfig.context->Supports(feature);
816 }
817
FiniVDPAUOutput()818 void CDecoder::FiniVDPAUOutput()
819 {
820 if (!m_vdpauConfigured)
821 return;
822
823 CLog::Log(LOGINFO, " (VDPAU) %s", __FUNCTION__);
824
825 // uninit output
826 m_vdpauOutput.Dispose();
827 m_vdpauConfigured = false;
828
829 VdpStatus vdp_st;
830
831 vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_destroy(m_vdpauConfig.vdpDecoder);
832 if (CheckStatus(vdp_st, __LINE__))
833 return;
834 m_vdpauConfig.vdpDecoder = VDP_INVALID_HANDLE;
835
836 CLog::Log(LOGDEBUG, LOGVIDEO, "CVDPAU::FiniVDPAUOutput destroying %d video surfaces", m_videoSurfaces.Size());
837
838 VdpVideoSurface surf;
839 while((surf = m_videoSurfaces.RemoveNext()) != VDP_INVALID_HANDLE)
840 {
841 m_vdpauConfig.context->GetProcs().vdp_video_surface_destroy(surf);
842 if (CheckStatus(vdp_st, __LINE__))
843 return;
844 }
845 m_videoSurfaces.Reset();
846 }
847
ReadFormatOf(AVCodecID codec,VdpDecoderProfile & vdp_decoder_profile,VdpChromaType & vdp_chroma_type)848 void CDecoder::ReadFormatOf( AVCodecID codec
849 , VdpDecoderProfile &vdp_decoder_profile
850 , VdpChromaType &vdp_chroma_type)
851 {
852 switch (codec)
853 {
854 case AV_CODEC_ID_MPEG1VIDEO:
855 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
856 vdp_chroma_type = VDP_CHROMA_TYPE_420;
857 break;
858 case AV_CODEC_ID_MPEG2VIDEO:
859 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
860 vdp_chroma_type = VDP_CHROMA_TYPE_420;
861 break;
862 case AV_CODEC_ID_H264:
863 vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
864 vdp_chroma_type = VDP_CHROMA_TYPE_420;
865 break;
866 #ifdef VDP_DECODER_PROFILE_HEVC_MAIN
867 case AV_CODEC_ID_HEVC:
868 vdp_decoder_profile = VDP_DECODER_PROFILE_HEVC_MAIN;
869 vdp_chroma_type = VDP_CHROMA_TYPE_420;
870 break;
871 #endif
872 #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_0
873 case AV_CODEC_ID_VP9:
874 vdp_decoder_profile = VDP_DECODER_PROFILE_VP9_PROFILE_0;
875 vdp_chroma_type = VDP_CHROMA_TYPE_420;
876 break;
877 #endif
878 case AV_CODEC_ID_WMV3:
879 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
880 vdp_chroma_type = VDP_CHROMA_TYPE_420;
881 break;
882 case AV_CODEC_ID_VC1:
883 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
884 vdp_chroma_type = VDP_CHROMA_TYPE_420;
885 break;
886 case AV_CODEC_ID_MPEG4:
887 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
888 vdp_chroma_type = VDP_CHROMA_TYPE_420;
889 break;
890 default:
891 vdp_decoder_profile = 0;
892 vdp_chroma_type = 0;
893 break;
894 }
895 }
896
ConfigVDPAU(AVCodecContext * avctx,int ref_frames)897 bool CDecoder::ConfigVDPAU(AVCodecContext* avctx, int ref_frames)
898 {
899 FiniVDPAUOutput();
900
901 VdpStatus vdp_st;
902 VdpDecoderProfile vdp_decoder_profile;
903
904 m_vdpauConfig.vidWidth = avctx->width;
905 m_vdpauConfig.vidHeight = avctx->height;
906 m_vdpauConfig.surfaceWidth = avctx->coded_width;
907 m_vdpauConfig.surfaceHeight = avctx->coded_height;
908
909 SetWidthHeight(avctx->width,avctx->height);
910
911 CLog::Log(LOGINFO, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i", m_vdpauConfig.outWidth,
912 m_vdpauConfig.vidWidth, m_vdpauConfig.surfaceWidth);
913 CLog::Log(LOGINFO, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",
914 m_vdpauConfig.outHeight, m_vdpauConfig.vidHeight, m_vdpauConfig.surfaceHeight);
915
916 ReadFormatOf(avctx->codec_id, vdp_decoder_profile, m_vdpauConfig.vdpChromaType);
917
918 if (avctx->codec_id == AV_CODEC_ID_H264)
919 {
920 m_vdpauConfig.maxReferences = ref_frames;
921 if (m_vdpauConfig.maxReferences > 16) m_vdpauConfig.maxReferences = 16;
922 if (m_vdpauConfig.maxReferences < 5) m_vdpauConfig.maxReferences = 5;
923 }
924 else if (avctx->codec_id == AV_CODEC_ID_HEVC)
925 {
926 // The DPB works quite differently in hevc and there isn't a per-file max
927 // reference number, so we force the maximum number (source: upstream ffmpeg)
928 m_vdpauConfig.maxReferences = 16;
929 }
930 else if (avctx->codec_id == AV_CODEC_ID_VP9)
931 {
932 if (avctx->profile != FF_PROFILE_VP9_0)
933 return false;
934
935 m_vdpauConfig.maxReferences = 8;
936 }
937 else
938 m_vdpauConfig.maxReferences = 2;
939
940 vdp_st = m_vdpauConfig.context->GetProcs().vdp_decoder_create(m_vdpauConfig.context->GetDevice(),
941 vdp_decoder_profile,
942 m_vdpauConfig.surfaceWidth,
943 m_vdpauConfig.surfaceHeight,
944 m_vdpauConfig.maxReferences,
945 &m_vdpauConfig.vdpDecoder);
946 if (CheckStatus(vdp_st, __LINE__))
947 return false;
948
949 // initialize output
950 CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
951 m_vdpauConfig.stats = &m_bufferStats;
952 m_vdpauConfig.vdpau = this;
953 m_bufferStats.Reset();
954 m_vdpauOutput.Start();
955 Message *reply;
956 if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT,
957 &reply,
958 2000,
959 &m_vdpauConfig,
960 sizeof(m_vdpauConfig)))
961 {
962 bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
963 reply->Release();
964 if (!success)
965 {
966 CLog::Log(LOGERROR, "VDPAU::%s - vdpau output returned error", __FUNCTION__);
967 m_vdpauOutput.Dispose();
968 return false;
969 }
970 }
971 else
972 {
973 CLog::Log(LOGERROR, "VDPAU::%s - failed to init output", __FUNCTION__);
974 m_vdpauOutput.Dispose();
975 return false;
976 }
977
978 m_inMsgEvent.Reset();
979 m_vdpauConfigured = true;
980 m_ErrorCount = 0;
981 m_vdpauConfig.resetCounter++;
982 return true;
983 }
984
FFGetBuffer(AVCodecContext * avctx,AVFrame * pic,int flags)985 int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags)
986 {
987 ICallbackHWAccel* cb = static_cast<ICallbackHWAccel*>(avctx->opaque);
988 CDecoder* vdp = static_cast<CDecoder*>(cb->GetHWAccel());
989
990 // while we are waiting to recover we can't do anything
991 CSingleLock lock(vdp->m_DecoderSection);
992
993 if(vdp->m_DisplayState != VDPAU_OPEN)
994 {
995 CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery");
996 return -1;
997 }
998
999 VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pic->data[3];
1000 surf = vdp->m_videoSurfaces.GetFree(surf != 0 ? surf : VDP_INVALID_HANDLE);
1001
1002 VdpStatus vdp_st = VDP_STATUS_ERROR;
1003 if (surf == VDP_INVALID_HANDLE)
1004 {
1005 // create a new surface
1006 VdpDecoderProfile profile;
1007 ReadFormatOf(avctx->codec_id, profile, vdp->m_vdpauConfig.vdpChromaType);
1008
1009 vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_video_surface_create(vdp->m_vdpauConfig.context->GetDevice(),
1010 vdp->m_vdpauConfig.vdpChromaType,
1011 avctx->coded_width,
1012 avctx->coded_height,
1013 &surf);
1014 vdp->CheckStatus(vdp_st, __LINE__);
1015 if (vdp_st != VDP_STATUS_OK)
1016 {
1017 CLog::Log(LOGERROR, "CVDPAU::FFGetBuffer - No Video surface available could be created");
1018 return -1;
1019 }
1020 vdp->m_videoSurfaces.AddSurface(surf);
1021 }
1022
1023 pic->data[1] = pic->data[2] = NULL;
1024 pic->data[0] = (uint8_t*)(uintptr_t)surf;
1025 pic->data[3] = (uint8_t*)(uintptr_t)surf;
1026 pic->linesize[0] = pic->linesize[1] = pic->linesize[2] = 0;
1027 AVBufferRef *buffer = av_buffer_create(pic->data[3], 0, FFReleaseBuffer, vdp, 0);
1028 if (!buffer)
1029 {
1030 CLog::Log(LOGERROR, "CVDPAU::%s - error creating buffer", __FUNCTION__);
1031 return -1;
1032 }
1033 pic->buf[0] = buffer;
1034
1035 pic->reordered_opaque= avctx->reordered_opaque;
1036 return 0;
1037 }
1038
FFReleaseBuffer(void * opaque,uint8_t * data)1039 void CDecoder::FFReleaseBuffer(void *opaque, uint8_t *data)
1040 {
1041 CDecoder *vdp = static_cast<CDecoder*>(opaque);
1042
1043 VdpVideoSurface surf;
1044
1045 CSingleLock lock(vdp->m_DecoderSection);
1046
1047 surf = (VdpVideoSurface)(uintptr_t)data;
1048
1049 vdp->m_videoSurfaces.ClearReference(surf);
1050 }
1051
Render(struct AVCodecContext * s,struct AVFrame * src,const VdpPictureInfo * info,uint32_t buffers_used,const VdpBitstreamBuffer * buffers)1052 int CDecoder::Render(struct AVCodecContext *s, struct AVFrame *src,
1053 const VdpPictureInfo *info, uint32_t buffers_used,
1054 const VdpBitstreamBuffer *buffers)
1055 {
1056 ICallbackHWAccel* ctx = static_cast<ICallbackHWAccel*>(s->opaque);
1057 CDecoder* vdp = static_cast<CDecoder*>(ctx->GetHWAccel());
1058
1059 // while we are waiting to recover we can't do anything
1060 CSingleLock lock(vdp->m_DecoderSection);
1061
1062 if(vdp->m_DisplayState != VDPAU_OPEN)
1063 return -1;
1064
1065 if(src->linesize[0] || src->linesize[1] || src->linesize[2])
1066 {
1067 CLog::Log(LOGERROR, "CVDPAU::FFDrawSlice - invalid linesizes or offsets provided");
1068 return -1;
1069 }
1070
1071 VdpStatus vdp_st;
1072 VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)src->data[3];
1073
1074 // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1075 if (!vdp->m_videoSurfaces.IsValid(surf))
1076 {
1077 CLog::Log(LOGWARNING, "CVDPAU::FFDrawSlice - ignoring invalid buffer");
1078 return -1;
1079 }
1080
1081 uint32_t max_refs = 0;
1082 if(s->codec_id == AV_CODEC_ID_H264)
1083 max_refs = s->refs;
1084
1085 if(vdp->m_vdpauConfig.vdpDecoder == VDP_INVALID_HANDLE
1086 || vdp->m_vdpauConfigured == false
1087 || vdp->m_vdpauConfig.maxReferences < max_refs)
1088 {
1089 if(!vdp->ConfigVDPAU(s, max_refs))
1090 return -1;
1091 }
1092
1093 uint64_t startTime = CurrentHostCounter();
1094 uint16_t decoded, processed, rend;
1095 vdp->m_bufferStats.Get(decoded, processed, rend);
1096 vdp_st = vdp->m_vdpauConfig.context->GetProcs().vdp_decoder_render(vdp->m_vdpauConfig.vdpDecoder,
1097 surf, info, buffers_used, buffers);
1098 if (vdp->CheckStatus(vdp_st, __LINE__))
1099 return -1;
1100
1101 uint64_t diff = CurrentHostCounter() - startTime;
1102 if (diff*1000/CurrentHostFrequency() > 30)
1103 CLog::Log(LOGDEBUG, LOGVIDEO, "CVDPAU::DrawSlice - VdpDecoderRender long decoding: %d ms, dec: %d, proc: %d, rend: %d", (int)((diff*1000)/CurrentHostFrequency()), decoded, processed, rend);
1104
1105 return 0;
1106 }
1107
SetCodecControl(int flags)1108 void CDecoder::SetCodecControl(int flags)
1109 {
1110 m_codecControl = flags & (DVD_CODEC_CTRL_DRAIN | DVD_CODEC_CTRL_HURRY);
1111 if (m_codecControl & DVD_CODEC_CTRL_DRAIN)
1112 m_bufferStats.SetDraining(true);
1113 else
1114 m_bufferStats.SetDraining(false);
1115 }
1116
Decode(AVCodecContext * avctx,AVFrame * pFrame)1117 CDVDVideoCodec::VCReturn CDecoder::Decode(AVCodecContext *avctx, AVFrame *pFrame)
1118 {
1119 CDVDVideoCodec::VCReturn result = Check(avctx);
1120 if (result != CDVDVideoCodec::VC_NONE)
1121 return result;
1122
1123 CSingleLock lock(m_DecoderSection);
1124
1125 if (!m_vdpauConfigured)
1126 return CDVDVideoCodec::VC_ERROR;
1127
1128 if(pFrame)
1129 { // we have a new frame from decoder
1130
1131 VdpVideoSurface surf = (VdpVideoSurface)(uintptr_t)pFrame->data[3];
1132 // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
1133 if (!m_videoSurfaces.IsValid(surf))
1134 {
1135 CLog::Log(LOGWARNING, "CVDPAU::Decode - ignoring invalid buffer");
1136 return CDVDVideoCodec::VC_BUFFER;
1137 }
1138 m_videoSurfaces.MarkRender(surf);
1139
1140 // send frame to output for processing
1141 CVdpauDecodedPicture *pic = new CVdpauDecodedPicture();
1142 static_cast<ICallbackHWAccel*>(avctx->opaque)->GetPictureCommon(&(pic->DVDPic));
1143 m_codecControl = pic->DVDPic.iFlags & (DVD_CODEC_CTRL_HURRY | DVD_CODEC_CTRL_NO_POSTPROC);
1144 pic->videoSurface = surf;
1145 pic->DVDPic.color_space = avctx->colorspace;
1146 m_bufferStats.IncDecoded();
1147 CPayloadWrap<CVdpauDecodedPicture> *payload = new CPayloadWrap<CVdpauDecodedPicture>(pic);
1148 m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, payload);
1149 }
1150
1151 uint16_t decoded, processed, render;
1152 Message *msg;
1153 while (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1154 {
1155 if (msg->signal == COutputControlProtocol::ERROR)
1156 {
1157 m_DisplayState = VDPAU_ERROR;
1158 msg->Release();
1159 return CDVDVideoCodec::VC_BUFFER;
1160 }
1161 msg->Release();
1162 }
1163
1164 bool drain = (m_codecControl & DVD_CODEC_CTRL_DRAIN);
1165
1166 m_bufferStats.Get(decoded, processed, render);
1167 // if all pics are drained, break the loop by setting VC_EOF
1168 if (drain && decoded <= 0 && processed <= 0 && render <= 0)
1169 return CDVDVideoCodec::VC_EOF;
1170
1171 uint64_t startTime = CurrentHostCounter();
1172 while (true)
1173 {
1174 // first fill the buffers to keep vdpau busy
1175 // mixer will run with decoded >= 2. output is limited by number of output surfaces
1176 // In case mixer is bypassed we limit by looking at processed
1177 if (!drain && decoded < 3 && processed < 3)
1178 {
1179 return CDVDVideoCodec::VC_BUFFER;
1180 }
1181 else if (m_vdpauOutput.m_dataPort.ReceiveInMessage(&msg))
1182 {
1183 if (msg->signal == COutputDataProtocol::PICTURE)
1184 {
1185 if (m_presentPicture)
1186 {
1187 m_presentPicture->Release();
1188 m_presentPicture = nullptr;
1189 }
1190
1191 m_presentPicture = *(CVdpauRenderPicture**)msg->data;
1192 m_bufferStats.DecRender();
1193 msg->Release();
1194 uint64_t diff = CurrentHostCounter() - startTime;
1195 m_bufferStats.SetParams(diff, m_codecControl);
1196 return CDVDVideoCodec::VC_PICTURE;
1197 }
1198 msg->Release();
1199 }
1200 else if (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg))
1201 {
1202 if (msg->signal == COutputControlProtocol::STATS)
1203 {
1204 msg->Release();
1205 m_bufferStats.Get(decoded, processed, render);
1206 if (!drain && decoded < 3 && processed < 3)
1207 {
1208 return CDVDVideoCodec::VC_BUFFER;
1209 }
1210 }
1211 else
1212 {
1213 m_DisplayState = VDPAU_ERROR;
1214 msg->Release();
1215 return CDVDVideoCodec::VC_ERROR;
1216 }
1217 }
1218
1219 if (!m_inMsgEvent.WaitMSec(2000))
1220 break;
1221 }
1222
1223 CLog::Log(LOGERROR, "VDPAU::%s - timed out waiting for output message", __FUNCTION__);
1224 m_DisplayState = VDPAU_ERROR;
1225
1226 return CDVDVideoCodec::VC_ERROR;
1227 }
1228
GetPicture(AVCodecContext * avctx,VideoPicture * picture)1229 bool CDecoder::GetPicture(AVCodecContext* avctx, VideoPicture* picture)
1230 {
1231 if (picture->videoBuffer)
1232 {
1233 picture->videoBuffer->Release();
1234 picture->videoBuffer = nullptr;
1235 }
1236
1237 CSingleLock lock(m_DecoderSection);
1238
1239 if (m_DisplayState != VDPAU_OPEN)
1240 return false;
1241
1242 picture->SetParams(m_presentPicture->DVDPic);
1243 picture->videoBuffer = m_presentPicture;
1244 m_presentPicture = nullptr;
1245
1246 return true;
1247 }
1248
Reset()1249 void CDecoder::Reset()
1250 {
1251 CSingleLock lock(m_DecoderSection);
1252
1253 if (m_presentPicture)
1254 {
1255 m_presentPicture->Release();
1256 m_presentPicture = nullptr;
1257 }
1258
1259 if (!m_vdpauConfigured)
1260 return;
1261
1262 Message *reply;
1263 if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
1264 &reply,
1265 2000))
1266 {
1267 bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1268 reply->Release();
1269 if (!success)
1270 {
1271 CLog::Log(LOGERROR, "VDPAU::%s - flush returned error", __FUNCTION__);
1272 m_DisplayState = VDPAU_ERROR;
1273 }
1274 else
1275 m_bufferStats.Reset();
1276 }
1277 else
1278 {
1279 CLog::Log(LOGERROR, "VDPAU::%s - flush timed out", __FUNCTION__);
1280 m_DisplayState = VDPAU_ERROR;
1281 }
1282 }
1283
CanSkipDeint()1284 bool CDecoder::CanSkipDeint()
1285 {
1286 return m_bufferStats.CanSkipDeint();
1287 }
1288
ReturnRenderPicture(CVdpauRenderPicture * renderPic)1289 void CDecoder::ReturnRenderPicture(CVdpauRenderPicture *renderPic)
1290 {
1291 m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
1292 }
1293
CheckStatus(VdpStatus vdp_st,int line)1294 bool CDecoder::CheckStatus(VdpStatus vdp_st, int line)
1295 {
1296 if (vdp_st != VDP_STATUS_OK)
1297 {
1298 CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d",
1299 m_vdpauConfig.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__,
1300 line);
1301
1302 m_ErrorCount++;
1303
1304 if(m_DisplayState == VDPAU_OPEN)
1305 {
1306 if (vdp_st == VDP_STATUS_DISPLAY_PREEMPTED)
1307 {
1308 m_DisplayEvent.Reset();
1309 m_DisplayState = VDPAU_LOST;
1310 }
1311 else if (m_ErrorCount > 2)
1312 m_DisplayState = VDPAU_ERROR;
1313 }
1314
1315 return true;
1316 }
1317 m_ErrorCount = 0;
1318 return false;
1319 }
1320
Create(CDVDStreamInfo & hint,CProcessInfo & processInfo,AVPixelFormat fmt)1321 IHardwareDecoder* CDecoder::Create(CDVDStreamInfo &hint, CProcessInfo &processInfo, AVPixelFormat fmt)
1322 {
1323 if (CDecoder::IsVDPAUFormat(fmt) && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAU))
1324 return new VDPAU::CDecoder(processInfo);
1325
1326 return nullptr;
1327 }
1328
Register()1329 void CDecoder::Register()
1330 {
1331 CVDPAUContext *context;
1332 if (!CVDPAUContext::EnsureContext(&context))
1333 return;
1334
1335 context->Release();
1336
1337 m_capGeneral = true;
1338
1339 CDVDFactoryCodec::RegisterHWAccel("vdpau", CDecoder::Create);
1340
1341 std::string gpuvendor = CServiceBroker::GetRenderSystem()->GetRenderVendor();
1342 std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
1343 bool isNvidia = (gpuvendor.compare(0, 6, "nvidia") == 0);
1344
1345 auto settingsComponent = CServiceBroker::GetSettingsComponent();
1346 if (!settingsComponent)
1347 return;
1348
1349 auto settings = settingsComponent->GetSettings();
1350 if (!settings)
1351 return;
1352
1353 auto setting = settings->GetSetting(CSettings::SETTING_VIDEOPLAYER_USEVDPAU);
1354 if (!setting)
1355 CLog::Log(LOGERROR, "Failed to load setting for: {}", CSettings::SETTING_VIDEOPLAYER_USEVDPAU);
1356 else
1357 setting->SetVisible(true);
1358
1359 if (!isNvidia)
1360 {
1361 constexpr std::array<const char*, 4> vdpauSettings = {
1362 CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG4, CSettings::SETTING_VIDEOPLAYER_USEVDPAUVC1,
1363 CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG2, CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER};
1364
1365 for (const auto& vdpauSetting : vdpauSettings)
1366 {
1367 setting = settings->GetSetting(vdpauSetting);
1368 if (!setting)
1369 {
1370 CLog::Log(LOGERROR, "Failed to load setting for: {}", vdpauSetting);
1371 continue;
1372 }
1373
1374 setting->SetVisible(true);
1375 }
1376 }
1377 }
1378
1379 //-----------------------------------------------------------------------------
1380 // BufferPool
1381 //-----------------------------------------------------------------------------
1382
1383 class VDPAU::CVdpauBufferPool : public IVideoBufferPool
1384 {
1385 public:
1386 explicit CVdpauBufferPool(CDecoder &decoder);
1387 ~CVdpauBufferPool() override;
1388 CVideoBuffer* Get() override;
1389 void Return(int id) override;
1390 CVdpauRenderPicture* GetVdpau();
1391 bool HasFree();
1392 void QueueReturnPicture(CVdpauRenderPicture *pic);
1393 CVdpauRenderPicture* ProcessSyncPicture();
1394 void InvalidateUsed();
1395
1396 unsigned short numOutputSurfaces;
1397 std::vector<VdpOutputSurface> outputSurfaces;
1398 std::queue<CVdpauProcessedPicture> processedPics;
1399 std::deque<CVdpauProcessedPicture> processedPicsAway;
1400
1401 int procPicId = 0;
1402
1403 protected:
1404 std::vector<CVdpauRenderPicture*> allRenderPics;
1405 std::deque<int> usedRenderPics;
1406 std::deque<int> freeRenderPics;
1407 std::deque<int> syncRenderPics;
1408
1409 CDecoder &m_vdpau;
1410 };
1411
CVdpauBufferPool(CDecoder & decoder)1412 CVdpauBufferPool::CVdpauBufferPool(CDecoder &decoder)
1413 : m_vdpau(decoder)
1414 {
1415 CVdpauRenderPicture *pic;
1416 for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1417 {
1418 pic = new CVdpauRenderPicture(i);
1419 allRenderPics.push_back(pic);
1420 freeRenderPics.push_back(i);
1421 }
1422 }
1423
~CVdpauBufferPool()1424 CVdpauBufferPool::~CVdpauBufferPool()
1425 {
1426 CVdpauRenderPicture *pic;
1427 for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1428 {
1429 pic = allRenderPics[i];
1430 delete pic;
1431 }
1432 allRenderPics.clear();
1433 }
1434
Get()1435 CVideoBuffer* CVdpauBufferPool::Get()
1436 {
1437 if (freeRenderPics.empty())
1438 return nullptr;
1439
1440 int idx = freeRenderPics.front();
1441 freeRenderPics.pop_front();
1442 usedRenderPics.push_back(idx);
1443
1444 CVideoBuffer *retPic = allRenderPics[idx];
1445 retPic->Acquire(GetPtr());
1446
1447 m_vdpau.Acquire();
1448
1449 return retPic;
1450 }
1451
Return(int id)1452 void CVdpauBufferPool::Return(int id)
1453 {
1454 CVdpauRenderPicture *pic = allRenderPics[id];
1455
1456 m_vdpau.ReturnRenderPicture(pic);
1457 m_vdpau.ReleasePicReference();
1458 }
1459
GetVdpau()1460 CVdpauRenderPicture* CVdpauBufferPool::GetVdpau()
1461 {
1462 return dynamic_cast<CVdpauRenderPicture*>(Get());
1463 }
1464
HasFree()1465 bool CVdpauBufferPool::HasFree()
1466 {
1467 return !freeRenderPics.empty();
1468 }
1469
QueueReturnPicture(CVdpauRenderPicture * pic)1470 void CVdpauBufferPool::QueueReturnPicture(CVdpauRenderPicture *pic)
1471 {
1472 std::deque<int>::iterator it;
1473 for (it = usedRenderPics.begin(); it != usedRenderPics.end(); ++it)
1474 {
1475 if (allRenderPics[*it] == pic)
1476 {
1477 break;
1478 }
1479 }
1480
1481 if (it == usedRenderPics.end())
1482 {
1483 CLog::Log(LOGWARNING, "COutput::QueueReturnPicture - pic not found");
1484 return;
1485 }
1486
1487 // check if already queued
1488 std::deque<int>::iterator it2 = find(syncRenderPics.begin(),
1489 syncRenderPics.end(),
1490 *it);
1491 if (it2 == syncRenderPics.end())
1492 {
1493 syncRenderPics.push_back(*it);
1494 }
1495 }
1496
ProcessSyncPicture()1497 CVdpauRenderPicture* CVdpauBufferPool::ProcessSyncPicture()
1498 {
1499 CVdpauRenderPicture *retPic = nullptr;
1500
1501 std::deque<int>::iterator it;
1502 for (it = syncRenderPics.begin(); it != syncRenderPics.end(); )
1503 {
1504 retPic = allRenderPics[*it];
1505
1506 freeRenderPics.push_back(*it);
1507
1508 std::deque<int>::iterator it2 = find(usedRenderPics.begin(),
1509 usedRenderPics.end(),
1510 *it);
1511 if (it2 == usedRenderPics.end())
1512 {
1513 CLog::Log(LOGERROR, "COutput::ProcessSyncPicture - pic not found in queue");
1514 }
1515 else
1516 {
1517 usedRenderPics.erase(it2);
1518 }
1519 it = syncRenderPics.erase(it);
1520
1521 break;
1522 }
1523 return retPic;
1524 }
1525
InvalidateUsed()1526 void CVdpauBufferPool::InvalidateUsed()
1527 {
1528 std::deque<int>::iterator it;
1529 for (it = usedRenderPics.begin(); it != usedRenderPics.end(); ++it)
1530 {
1531 allRenderPics[*it]->procPic.outputSurface = VDP_INVALID_HANDLE;
1532 allRenderPics[*it]->procPic.videoSurface = VDP_INVALID_HANDLE;
1533 }
1534 }
1535
1536 //-----------------------------------------------------------------------------
1537 // Mixer
1538 //-----------------------------------------------------------------------------
CMixer(CEvent * inMsgEvent)1539 CMixer::CMixer(CEvent *inMsgEvent) :
1540 CThread("Vdpau Mixer"),
1541 m_controlPort("ControlPort", inMsgEvent, &m_outMsgEvent),
1542 m_dataPort("DataPort", inMsgEvent, &m_outMsgEvent)
1543 {
1544 m_inMsgEvent = inMsgEvent;
1545 }
1546
~CMixer()1547 CMixer::~CMixer()
1548 {
1549 Dispose();
1550 }
1551
Start()1552 void CMixer::Start()
1553 {
1554 Create();
1555 }
1556
Dispose()1557 void CMixer::Dispose()
1558 {
1559 m_bStop = true;
1560 m_outMsgEvent.Set();
1561 StopThread();
1562
1563 m_controlPort.Purge();
1564 m_dataPort.Purge();
1565 }
1566
IsActive()1567 bool CMixer::IsActive()
1568 {
1569 return IsRunning();
1570 }
1571
OnStartup()1572 void CMixer::OnStartup()
1573 {
1574 CLog::Log(LOGINFO, "CMixer::OnStartup: Output Thread created");
1575 }
1576
OnExit()1577 void CMixer::OnExit()
1578 {
1579 CLog::Log(LOGINFO, "CMixer::OnExit: Output Thread terminated");
1580 }
1581
1582 enum MIXER_STATES
1583 {
1584 M_TOP = 0, // 0
1585 M_TOP_ERROR, // 1
1586 M_TOP_UNCONFIGURED, // 2
1587 M_TOP_CONFIGURED, // 3
1588 M_TOP_CONFIGURED_WAIT1, // 4
1589 M_TOP_CONFIGURED_STEP1, // 5
1590 M_TOP_CONFIGURED_WAIT2, // 6
1591 M_TOP_CONFIGURED_STEP2, // 7
1592 };
1593
1594 int MIXER_parentStates[] = {
1595 -1,
1596 0, //TOP_ERROR
1597 0, //TOP_UNCONFIGURED
1598 0, //TOP_CONFIGURED
1599 3, //TOP_CONFIGURED_WAIT1
1600 3, //TOP_CONFIGURED_STEP1
1601 3, //TOP_CONFIGURED_WAIT2
1602 3, //TOP_CONFIGURED_STEP2
1603 };
1604
StateMachine(int signal,Protocol * port,Message * msg)1605 void CMixer::StateMachine(int signal, Protocol *port, Message *msg)
1606 {
1607 for (int state = m_state; ; state = MIXER_parentStates[state])
1608 {
1609 switch (state)
1610 {
1611 case M_TOP: // TOP
1612 if (port == &m_controlPort)
1613 {
1614 switch (signal)
1615 {
1616 case CMixerControlProtocol::FLUSH:
1617 Flush();
1618 msg->Reply(CMixerControlProtocol::ACC);
1619 return;
1620 default:
1621 break;
1622 }
1623 }
1624 {
1625 std::string portName = port == NULL ? "timer" : port->portName;
1626 CLog::Log(LOGWARNING, "CMixer::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
1627 }
1628 return;
1629
1630 case M_TOP_ERROR: // TOP
1631 break;
1632
1633 case M_TOP_UNCONFIGURED:
1634 if (port == &m_controlPort)
1635 {
1636 switch (signal)
1637 {
1638 case CMixerControlProtocol::INIT:
1639 CVdpauConfig *data;
1640 data = (CVdpauConfig*)msg->data;
1641 if (data)
1642 {
1643 m_config = *data;
1644 }
1645 Init();
1646 if (!m_vdpError)
1647 {
1648 m_state = M_TOP_CONFIGURED_WAIT1;
1649 msg->Reply(CMixerControlProtocol::ACC);
1650 }
1651 else
1652 {
1653 msg->Reply(CMixerControlProtocol::ERROR);
1654 }
1655 return;
1656 default:
1657 break;
1658 }
1659 }
1660 break;
1661
1662 case M_TOP_CONFIGURED:
1663 if (port == &m_controlPort)
1664 {
1665 switch (signal)
1666 {
1667 case CMixerControlProtocol::FLUSH:
1668 Flush();
1669 msg->Reply(CMixerControlProtocol::ACC);
1670 m_state = M_TOP_CONFIGURED_WAIT1;
1671 return;
1672 default:
1673 break;
1674 }
1675 }
1676 else if (port == &m_dataPort)
1677 {
1678 switch (signal)
1679 {
1680 case CMixerDataProtocol::FRAME:
1681 CPayloadWrap<CVdpauDecodedPicture> *payload;
1682 payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.get());
1683 if (payload)
1684 {
1685 m_decodedPics.push(*(payload->GetPlayload()));
1686 }
1687 m_extTimeout = 0;
1688 return;
1689 case CMixerDataProtocol::BUFFER:
1690 VdpOutputSurface *surf;
1691 surf = (VdpOutputSurface*)msg->data;
1692 if (surf)
1693 {
1694 m_outputSurfaces.push(*surf);
1695 }
1696 m_extTimeout = 0;
1697 return;
1698 default:
1699 break;
1700 }
1701 }
1702 break;
1703
1704 case M_TOP_CONFIGURED_WAIT1:
1705 if (port == NULL) // timeout
1706 {
1707 switch (signal)
1708 {
1709 case CMixerControlProtocol::TIMEOUT:
1710 if (!m_decodedPics.empty() && !m_outputSurfaces.empty())
1711 {
1712 m_state = M_TOP_CONFIGURED_STEP1;
1713 m_bStateMachineSelfTrigger = true;
1714 }
1715 else if (!m_outputSurfaces.empty() &&
1716 m_config.stats->IsDraining() &&
1717 m_mixerInput.size() >= 1)
1718 {
1719 CVdpauDecodedPicture pic;
1720 pic.DVDPic.SetParams(m_mixerInput[0].DVDPic);
1721 pic.videoSurface = VDP_INVALID_HANDLE;
1722 m_decodedPics.push(pic);
1723 m_state = M_TOP_CONFIGURED_STEP1;
1724 m_bStateMachineSelfTrigger = true;
1725 }
1726 else
1727 {
1728 m_extTimeout = 100;
1729 }
1730 return;
1731 default:
1732 break;
1733 }
1734 }
1735 break;
1736
1737 case M_TOP_CONFIGURED_STEP1:
1738 if (port == NULL) // timeout
1739 {
1740 switch (signal)
1741 {
1742 case CMixerControlProtocol::TIMEOUT:
1743 m_mixerInput.push_front(m_decodedPics.front());
1744 m_decodedPics.pop();
1745 if (m_mixerInput.size() < 2)
1746 {
1747 m_state = M_TOP_CONFIGURED_WAIT1;
1748 m_extTimeout = 0;
1749 return;
1750 }
1751 InitCycle();
1752 ProcessPicture();
1753 if (m_vdpError)
1754 {
1755 m_state = M_TOP_CONFIGURED_WAIT1;
1756 m_extTimeout = 1000;
1757 return;
1758 }
1759 if (!m_processPicture.isYuv)
1760 m_outputSurfaces.pop();
1761 m_config.stats->IncProcessed();
1762 m_config.stats->DecDecoded();
1763 m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1764 if (m_mixersteps > 1)
1765 {
1766 m_state = M_TOP_CONFIGURED_WAIT2;
1767 m_extTimeout = 0;
1768 }
1769 else
1770 {
1771 FiniCycle();
1772 m_state = M_TOP_CONFIGURED_WAIT1;
1773 m_extTimeout = 0;
1774 }
1775 return;
1776 default:
1777 break;
1778 }
1779 }
1780 break;
1781
1782 case M_TOP_CONFIGURED_WAIT2:
1783 if (port == NULL) // timeout
1784 {
1785 switch (signal)
1786 {
1787 case CMixerControlProtocol::TIMEOUT:
1788 if (!m_outputSurfaces.empty())
1789 {
1790 m_state = M_TOP_CONFIGURED_STEP2;
1791 m_bStateMachineSelfTrigger = true;
1792 }
1793 else
1794 {
1795 m_extTimeout = 100;
1796 }
1797 return;
1798 default:
1799 break;
1800 }
1801 }
1802 break;
1803
1804 case M_TOP_CONFIGURED_STEP2:
1805 if (port == NULL) // timeout
1806 {
1807 switch (signal)
1808 {
1809 case CMixerControlProtocol::TIMEOUT:
1810 m_processPicture.outputSurface = m_outputSurfaces.front();
1811 m_mixerstep = 1;
1812 ProcessPicture();
1813 if (m_vdpError)
1814 {
1815 m_state = M_TOP_CONFIGURED_WAIT1;
1816 m_extTimeout = 1000;
1817 return;
1818 }
1819 if (!m_processPicture.isYuv)
1820 m_outputSurfaces.pop();
1821 m_config.stats->IncProcessed();
1822 m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture));
1823 FiniCycle();
1824 m_state = M_TOP_CONFIGURED_WAIT1;
1825 m_extTimeout = 0;
1826 return;
1827 default:
1828 break;
1829 }
1830 }
1831 break;
1832
1833 default: // we are in no state, should not happen
1834 CLog::Log(LOGERROR, "CMixer::%s - no valid state: %d", __FUNCTION__, m_state);
1835 return;
1836 }
1837 } // for
1838 }
1839
Process()1840 void CMixer::Process()
1841 {
1842 Message *msg = NULL;
1843 Protocol *port = NULL;
1844 bool gotMsg;
1845
1846 m_state = M_TOP_UNCONFIGURED;
1847 m_extTimeout = 1000;
1848 m_bStateMachineSelfTrigger = false;
1849
1850 while (!m_bStop)
1851 {
1852 gotMsg = false;
1853
1854 if (m_bStateMachineSelfTrigger)
1855 {
1856 m_bStateMachineSelfTrigger = false;
1857 // self trigger state machine
1858 StateMachine(msg->signal, port, msg);
1859 if (!m_bStateMachineSelfTrigger)
1860 {
1861 msg->Release();
1862 msg = NULL;
1863 }
1864 continue;
1865 }
1866 // check control port
1867 else if (m_controlPort.ReceiveOutMessage(&msg))
1868 {
1869 gotMsg = true;
1870 port = &m_controlPort;
1871 }
1872 // check data port
1873 else if (m_dataPort.ReceiveOutMessage(&msg))
1874 {
1875 gotMsg = true;
1876 port = &m_dataPort;
1877 }
1878
1879 if (gotMsg)
1880 {
1881 StateMachine(msg->signal, port, msg);
1882 if (!m_bStateMachineSelfTrigger)
1883 {
1884 msg->Release();
1885 msg = NULL;
1886 }
1887 continue;
1888 }
1889
1890 // wait for message
1891 else if (m_outMsgEvent.WaitMSec(m_extTimeout))
1892 {
1893 continue;
1894 }
1895 // time out
1896 else
1897 {
1898 msg = m_controlPort.GetMessage();
1899 msg->signal = CMixerControlProtocol::TIMEOUT;
1900 port = 0;
1901 // signal timeout to state machine
1902 StateMachine(msg->signal, port, msg);
1903 if (!m_bStateMachineSelfTrigger)
1904 {
1905 msg->Release();
1906 msg = NULL;
1907 }
1908 }
1909 }
1910 Uninit();
1911 }
1912
CreateVdpauMixer()1913 void CMixer::CreateVdpauMixer()
1914 {
1915 CLog::Log(LOGINFO, " (VDPAU) Creating the video mixer");
1916
1917 InitCSCMatrix(m_config.vidWidth);
1918
1919 VdpVideoMixerParameter parameters[] = {
1920 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
1921 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
1922 VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE};
1923
1924 void const * parameter_values[] = {
1925 &m_config.surfaceWidth,
1926 &m_config.surfaceHeight,
1927 &m_config.vdpChromaType};
1928
1929 VdpStatus vdp_st = m_config.context->GetProcs().vdp_video_mixer_create(m_config.context->GetDevice(),
1930 m_config.context->GetFeatureCount(),
1931 m_config.context->GetFeatures(),
1932 ARSIZE(parameters),
1933 parameters,
1934 parameter_values,
1935 &m_videoMixer);
1936 CheckStatus(vdp_st, __LINE__);
1937
1938 }
1939
InitCSCMatrix(int Width)1940 void CMixer::InitCSCMatrix(int Width)
1941 {
1942 m_Procamp.struct_version = VDP_PROCAMP_VERSION;
1943 m_Procamp.brightness = 0.0;
1944 m_Procamp.contrast = 1.0;
1945 m_Procamp.saturation = 1.0;
1946 m_Procamp.hue = 0;
1947 }
1948
CheckFeatures()1949 void CMixer::CheckFeatures()
1950 {
1951 if (m_Upscale != m_config.upscale)
1952 {
1953 SetHWUpscaling();
1954 m_Upscale = m_config.upscale;
1955 }
1956 if (m_Brightness != m_config.processInfo->GetVideoSettings().m_Brightness ||
1957 m_Contrast != m_config.processInfo->GetVideoSettings().m_Contrast ||
1958 m_ColorMatrix != m_mixerInput[1].DVDPic.color_space)
1959 {
1960 SetColor();
1961 m_Brightness = m_config.processInfo->GetVideoSettings().m_Brightness;
1962 m_Contrast = m_config.processInfo->GetVideoSettings().m_Contrast;
1963 m_ColorMatrix = m_mixerInput[1].DVDPic.color_space;
1964 }
1965 if (m_NoiseReduction != m_config.processInfo->GetVideoSettings().m_NoiseReduction)
1966 {
1967 m_NoiseReduction = m_config.processInfo->GetVideoSettings().m_NoiseReduction;
1968 SetNoiseReduction();
1969 }
1970 if (m_Sharpness != m_config.processInfo->GetVideoSettings().m_Sharpness)
1971 {
1972 m_Sharpness = m_config.processInfo->GetVideoSettings().m_Sharpness;
1973 SetSharpness();
1974 }
1975 if (m_Deint != m_config.processInfo->GetVideoSettings().m_InterlaceMethod)
1976 {
1977 m_Deint = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
1978 SetDeinterlacing();
1979 }
1980 }
1981
SetPostProcFeatures(bool postProcEnabled)1982 void CMixer::SetPostProcFeatures(bool postProcEnabled)
1983 {
1984 if (m_PostProc != postProcEnabled)
1985 {
1986 if (postProcEnabled)
1987 {
1988 SetNoiseReduction();
1989 SetSharpness();
1990 SetDeinterlacing();
1991 SetHWUpscaling();
1992 }
1993 else
1994 PostProcOff();
1995 m_PostProc = postProcEnabled;
1996 }
1997 }
1998
PostProcOff()1999 void CMixer::PostProcOff()
2000 {
2001 VdpStatus vdp_st;
2002
2003 if (m_videoMixer == VDP_INVALID_HANDLE)
2004 return;
2005
2006 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
2007 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
2008 VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE};
2009
2010 VdpBool enabled[]={0,0,0};
2011 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2012 CheckStatus(vdp_st, __LINE__);
2013
2014 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
2015 {
2016 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION};
2017
2018 VdpBool enabled[]={0};
2019 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2020 CheckStatus(vdp_st, __LINE__);
2021 }
2022
2023 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
2024 {
2025 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS};
2026
2027 VdpBool enabled[]={0};
2028 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2029 CheckStatus(vdp_st, __LINE__);
2030 }
2031
2032 DisableHQScaling();
2033 }
2034
GenerateStudioCSCMatrix(VdpColorStandard colorStandard,VdpCSCMatrix & studioCSCMatrix)2035 bool CMixer::GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix)
2036 {
2037 // instead use studioCSCKCoeffs601[3], studioCSCKCoeffs709[3] to generate float[3][4] matrix (float studioCSC[3][4])
2038 // m00 = mRY = red: luma factor (contrast factor) (1.0)
2039 // m10 = mGY = green: luma factor (contrast factor) (1.0)
2040 // m20 = mBY = blue: luma factor (contrast factor) (1.0)
2041 //
2042 // m01 = mRB = red: blue color diff coeff (0.0)
2043 // m11 = mGB = green: blue color diff coeff (-2Kb(1-Kb)/(Kg))
2044 // m21 = mBB = blue: blue color diff coeff ((1-Kb)/0.5)
2045 //
2046 // m02 = mRR = red: red color diff coeff ((1-Kr)/0.5)
2047 // m12 = mGR = green: red color diff coeff (-2Kr(1-Kr)/(Kg))
2048 // m22 = mBR = blue: red color diff coeff (0.0)
2049 //
2050 // m03 = mRC = red: colour zero offset (brightness factor) (-(1-Kr)/0.5 * (128/255))
2051 // m13 = mGC = green: colour zero offset (brightness factor) ((256/255) * (Kb(1-Kb) + Kr(1-Kr)) / Kg)
2052 // m23 = mBC = blue: colour zero offset (brightness factor) (-(1-Kb)/0.5 * (128/255))
2053
2054 // columns
2055 int Y = 0;
2056 int Cb = 1;
2057 int Cr = 2;
2058 int C = 3;
2059 // rows
2060 int R = 0;
2061 int G = 1;
2062 int B = 2;
2063 // colour standard coefficients for red, geen, blue
2064 double Kr, Kg, Kb;
2065 // colour diff zero position (use standard 8-bit coding precision)
2066 double CDZ = 128; //256*0.5
2067 // range excursion (use standard 8-bit coding precision)
2068 double EXC = 255; //256-1
2069
2070 if (colorStandard == VDP_COLOR_STANDARD_ITUR_BT_601)
2071 {
2072 Kr = studioCSCKCoeffs601[0];
2073 Kg = studioCSCKCoeffs601[1];
2074 Kb = studioCSCKCoeffs601[2];
2075 }
2076 else // assume VDP_COLOR_STANDARD_ITUR_BT_709
2077 {
2078 Kr = studioCSCKCoeffs709[0];
2079 Kg = studioCSCKCoeffs709[1];
2080 Kb = studioCSCKCoeffs709[2];
2081 }
2082 // we keep luma unscaled to retain the levels present in source so that 16-235 luma is converted to RGB 16-235
2083 studioCSCMatrix[R][Y] = 1.0;
2084 studioCSCMatrix[G][Y] = 1.0;
2085 studioCSCMatrix[B][Y] = 1.0;
2086
2087 studioCSCMatrix[R][Cb] = 0.0;
2088 studioCSCMatrix[G][Cb] = (double)-2 * Kb * (1 - Kb) / Kg;
2089 studioCSCMatrix[B][Cb] = (double)(1 - Kb) / 0.5;
2090
2091 studioCSCMatrix[R][Cr] = (double)(1 - Kr) / 0.5;
2092 studioCSCMatrix[G][Cr] = (double)-2 * Kr * (1 - Kr) / Kg;
2093 studioCSCMatrix[B][Cr] = 0.0;
2094
2095 studioCSCMatrix[R][C] = (double)-1 * studioCSCMatrix[R][Cr] * CDZ/EXC;
2096 studioCSCMatrix[G][C] = (double)-1 * (studioCSCMatrix[G][Cb] + studioCSCMatrix[G][Cr]) * CDZ/EXC;
2097 studioCSCMatrix[B][C] = (double)-1 * studioCSCMatrix[B][Cb] * CDZ/EXC;
2098
2099 return true;
2100 }
2101
SetColor()2102 void CMixer::SetColor()
2103 {
2104 VdpStatus vdp_st;
2105
2106 if (m_Brightness != m_config.processInfo->GetVideoSettings().m_Brightness)
2107 m_Procamp.brightness = (float)((m_config.processInfo->GetVideoSettings().m_Brightness)-50) / 100;
2108 if (m_Contrast != m_config.processInfo->GetVideoSettings().m_Contrast)
2109 m_Procamp.contrast = (float)((m_config.processInfo->GetVideoSettings().m_Contrast)+50) / 100;
2110
2111 VdpColorStandard colorStandard;
2112 switch(m_mixerInput[1].DVDPic.color_space)
2113 {
2114 case AVCOL_SPC_BT709:
2115 colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
2116 break;
2117 case AVCOL_SPC_BT470BG:
2118 case AVCOL_SPC_SMPTE170M:
2119 colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
2120 break;
2121 case AVCOL_SPC_SMPTE240M:
2122 colorStandard = VDP_COLOR_STANDARD_SMPTE_240M;
2123 break;
2124 case AVCOL_SPC_FCC:
2125 case AVCOL_SPC_UNSPECIFIED:
2126 case AVCOL_SPC_RGB:
2127 default:
2128 if(m_config.surfaceWidth > 1000)
2129 colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709;
2130 else
2131 colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601;
2132 }
2133
2134 VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
2135 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE))
2136 {
2137 float studioCSC[3][4];
2138 GenerateStudioCSCMatrix(colorStandard, studioCSC);
2139 void const * pm_CSCMatrix[] = { &studioCSC };
2140 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatrix);
2141 }
2142 else
2143 {
2144 vdp_st = m_config.context->GetProcs().vdp_generate_csc_matrix(&m_Procamp, colorStandard, &m_CSCMatrix);
2145 if(vdp_st != VDP_STATUS_ERROR)
2146 {
2147 void const * pm_CSCMatrix[] = { &m_CSCMatrix };
2148 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatrix);
2149 }
2150 }
2151
2152 CheckStatus(vdp_st, __LINE__);
2153 }
2154
SetNoiseReduction()2155 void CMixer::SetNoiseReduction()
2156 {
2157 if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION))
2158 return;
2159
2160 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION };
2161 VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL };
2162 VdpStatus vdp_st;
2163
2164 if (!m_config.processInfo->GetVideoSettings().m_NoiseReduction)
2165 {
2166 VdpBool enabled[]= {0};
2167 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2168 CheckStatus(vdp_st, __LINE__);
2169 return;
2170 }
2171 VdpBool enabled[]={1};
2172 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2173 CheckStatus(vdp_st, __LINE__);
2174 float noiseReduction = m_config.processInfo->GetVideoSettings().m_NoiseReduction;
2175 void* nr[] = { &noiseReduction };
2176 CLog::Log(LOGINFO, "Setting Noise Reduction to %f",
2177 m_config.processInfo->GetVideoSettings().m_NoiseReduction);
2178 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, nr);
2179 CheckStatus(vdp_st, __LINE__);
2180 }
2181
SetSharpness()2182 void CMixer::SetSharpness()
2183 {
2184 if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS))
2185 return;
2186
2187 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS };
2188 VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL };
2189 VdpStatus vdp_st;
2190
2191 if (!m_config.processInfo->GetVideoSettings().m_Sharpness)
2192 {
2193 VdpBool enabled[]={0};
2194 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2195 CheckStatus(vdp_st, __LINE__);
2196 return;
2197 }
2198 VdpBool enabled[]={1};
2199 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2200 CheckStatus(vdp_st, __LINE__);
2201 float sharpness = m_config.processInfo->GetVideoSettings().m_Sharpness;
2202 void* sh[] = { &sharpness };
2203 CLog::Log(LOGINFO, "Setting Sharpness to %f",
2204 m_config.processInfo->GetVideoSettings().m_Sharpness);
2205 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, sh);
2206 CheckStatus(vdp_st, __LINE__);
2207 }
2208
SetDeinterlacing()2209 void CMixer::SetDeinterlacing()
2210 {
2211 VdpStatus vdp_st;
2212
2213 if (m_videoMixer == VDP_INVALID_HANDLE)
2214 return;
2215
2216 EINTERLACEMETHOD method = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
2217
2218 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
2219 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
2220 VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE };
2221
2222 if (method == VS_INTERLACEMETHOD_NONE)
2223 {
2224 VdpBool enabled[] = {0,0,0};
2225 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2226 }
2227 else
2228 {
2229 // fall back path if called with non supported method
2230 if (!m_config.processInfo->Supports(method))
2231 {
2232 method = VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
2233 m_Deint = VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
2234 }
2235
2236 if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
2237 || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
2238 {
2239 VdpBool enabled[] = {1,0,0};
2240 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUtelecine)
2241 enabled[2] = 1;
2242 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2243 }
2244 else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
2245 || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
2246 {
2247 VdpBool enabled[] = {1,1,0};
2248 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUtelecine)
2249 enabled[2] = 1;
2250 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2251 }
2252 else
2253 {
2254 VdpBool enabled[]={0,0,0};
2255 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2256 }
2257 }
2258 CheckStatus(vdp_st, __LINE__);
2259
2260 SetDeintSkipChroma();
2261
2262 m_config.useInteropYuv = !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER);
2263
2264 std::string deintStr = GetDeintStrFromInterlaceMethod(method);
2265 // update deinterlacing method used in processInfo (none if progressive)
2266 m_config.processInfo->SetVideoDeintMethod(deintStr);
2267 }
2268
SetDeintSkipChroma()2269 void CMixer::SetDeintSkipChroma()
2270 {
2271 VdpVideoMixerAttribute attribute[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE};
2272 VdpStatus vdp_st;
2273
2274 uint8_t val;
2275 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUdeintSkipChromaHD && m_config.outHeight >= 720)
2276 val = 1;
2277 else
2278 val = 0;
2279
2280 void const *values[]={&val};
2281 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attribute), attribute, values);
2282
2283 CheckStatus(vdp_st, __LINE__);
2284 }
2285
SetHWUpscaling()2286 void CMixer::SetHWUpscaling()
2287 {
2288 #ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
2289
2290 VdpStatus vdp_st;
2291 VdpBool enabled[]={1};
2292 switch (m_config.upscale)
2293 {
2294 case 9:
2295 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2296 {
2297 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2298 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2299 break;
2300 }
2301 case 8:
2302 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2303 {
2304 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2305 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2306 break;
2307 }
2308 case 7:
2309 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2310 {
2311 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2312 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2313 break;
2314 }
2315 case 6:
2316 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2317 {
2318 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2319 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2320 break;
2321 }
2322 case 5:
2323 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2324 {
2325 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2326 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2327 break;
2328 }
2329 case 4:
2330 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2331 {
2332 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2333 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2334 break;
2335 }
2336 case 3:
2337 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2338 {
2339 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2340 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2341 break;
2342 }
2343 case 2:
2344 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2345 {
2346 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2347 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2348 break;
2349 }
2350 case 1:
2351 if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2352 {
2353 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2354 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2355 break;
2356 }
2357 default:
2358 DisableHQScaling();
2359 return;
2360 }
2361 CheckStatus(vdp_st, __LINE__);
2362 #endif
2363 }
2364
DisableHQScaling()2365 void CMixer::DisableHQScaling()
2366 {
2367 VdpStatus vdp_st;
2368
2369 if (m_videoMixer == VDP_INVALID_HANDLE)
2370 return;
2371
2372 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1))
2373 {
2374 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 };
2375 VdpBool enabled[]={0};
2376 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2377 CheckStatus(vdp_st, __LINE__);
2378 }
2379
2380 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2))
2381 {
2382 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 };
2383 VdpBool enabled[]={0};
2384 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2385 CheckStatus(vdp_st, __LINE__);
2386 }
2387
2388 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3))
2389 {
2390 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 };
2391 VdpBool enabled[]={0};
2392 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2393 CheckStatus(vdp_st, __LINE__);
2394 }
2395
2396 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4))
2397 {
2398 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 };
2399 VdpBool enabled[]={0};
2400 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2401 CheckStatus(vdp_st, __LINE__);
2402 }
2403
2404 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5))
2405 {
2406 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 };
2407 VdpBool enabled[]={0};
2408 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2409 CheckStatus(vdp_st, __LINE__);
2410 }
2411
2412 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6))
2413 {
2414 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 };
2415 VdpBool enabled[]={0};
2416 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2417 CheckStatus(vdp_st, __LINE__);
2418 }
2419
2420 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7))
2421 {
2422 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 };
2423 VdpBool enabled[]={0};
2424 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2425 CheckStatus(vdp_st, __LINE__);
2426 }
2427
2428 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8))
2429 {
2430 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 };
2431 VdpBool enabled[]={0};
2432 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2433 CheckStatus(vdp_st, __LINE__);
2434 }
2435
2436 if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9))
2437 {
2438 VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 };
2439 VdpBool enabled[]={0};
2440 vdp_st = m_config.context->GetProcs().vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled);
2441 CheckStatus(vdp_st, __LINE__);
2442 }
2443 }
2444
Init()2445 void CMixer::Init()
2446 {
2447 m_Brightness = 0.0;
2448 m_Contrast = 0.0;
2449 m_NoiseReduction = 0.0;
2450 m_Sharpness = 0.0;
2451 m_Deint = 0;
2452 m_Upscale = 0;
2453 m_SeenInterlaceFlag = false;
2454 m_ColorMatrix = 0;
2455 m_PostProc = false;
2456 m_vdpError = false;
2457
2458 m_config.upscale = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoVDPAUScaling;
2459 m_config.useInteropYuv = !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER);
2460
2461 CreateVdpauMixer();
2462
2463 // update deinterlacing methods in processInfo
2464 std::list<EINTERLACEMETHOD> deintMethods;
2465 deintMethods.push_back(VS_INTERLACEMETHOD_NONE);
2466 for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
2467 {
2468 if (p->method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE)
2469 continue;
2470
2471 if (m_config.vdpau->Supports(p->feature))
2472 deintMethods.push_back(p->method);
2473 }
2474 deintMethods.push_back(VS_INTERLACEMETHOD_VDPAU_BOB);
2475 deintMethods.push_back(VS_INTERLACEMETHOD_RENDER_BOB);
2476 m_config.processInfo->UpdateDeinterlacingMethods(deintMethods);
2477 m_config.processInfo->SetDeinterlacingMethodDefault(EINTERLACEMETHOD::VS_INTERLACEMETHOD_VDPAU_TEMPORAL);
2478 }
2479
Uninit()2480 void CMixer::Uninit()
2481 {
2482 Flush();
2483 while (!m_outputSurfaces.empty())
2484 {
2485 m_outputSurfaces.pop();
2486 }
2487 m_config.context->GetProcs().vdp_video_mixer_destroy(m_videoMixer);
2488 }
2489
Flush()2490 void CMixer::Flush()
2491 {
2492 while (!m_mixerInput.empty())
2493 {
2494 CVdpauDecodedPicture pic = m_mixerInput.back();
2495 m_mixerInput.pop_back();
2496 m_config.videoSurfaces->ClearRender(pic.videoSurface);
2497 }
2498 while (!m_decodedPics.empty())
2499 {
2500 CVdpauDecodedPicture pic = m_decodedPics.front();
2501 m_decodedPics.pop();
2502 m_config.videoSurfaces->ClearRender(pic.videoSurface);
2503 }
2504 Message *msg;
2505 while (m_dataPort.ReceiveOutMessage(&msg))
2506 {
2507 if (msg->signal == CMixerDataProtocol::FRAME)
2508 {
2509 CPayloadWrap<CVdpauDecodedPicture> *payload;
2510 payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.get());
2511 if (payload)
2512 {
2513 CVdpauDecodedPicture pic = *(payload->GetPlayload());
2514 m_config.videoSurfaces->ClearRender(pic.videoSurface);
2515 }
2516 }
2517 else if (msg->signal == CMixerDataProtocol::BUFFER)
2518 {
2519 VdpOutputSurface *surf;
2520 surf = (VdpOutputSurface*)msg->data;
2521 m_outputSurfaces.push(*surf);
2522 }
2523 msg->Release();
2524 }
2525 }
2526
GetDeintStrFromInterlaceMethod(EINTERLACEMETHOD method)2527 std::string CMixer::GetDeintStrFromInterlaceMethod(EINTERLACEMETHOD method)
2528 {
2529 switch (method)
2530 {
2531 case VS_INTERLACEMETHOD_NONE:
2532 return "none";
2533 case VS_INTERLACEMETHOD_VDPAU_BOB:
2534 return "vdpau-bob";
2535 case VS_INTERLACEMETHOD_VDPAU_TEMPORAL:
2536 return "vdpau-temp";
2537 case VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF:
2538 return "vdpau-temp-half";
2539 case VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL:
2540 return "vdpau-temp-spat";
2541 case VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF:
2542 return "vdpau-temp-spat-half";
2543 case VS_INTERLACEMETHOD_RENDER_BOB:
2544 return "bob";
2545 default:
2546 return "unknown";
2547 }
2548 }
2549
InitCycle()2550 void CMixer::InitCycle()
2551 {
2552 CheckFeatures();
2553 int flags;
2554 uint64_t latency;
2555 m_config.stats->GetParams(latency, flags);
2556 if (flags & DVD_CODEC_CTRL_NO_POSTPROC)
2557 SetPostProcFeatures(false);
2558 else
2559 SetPostProcFeatures(true);
2560
2561 m_config.stats->SetCanSkipDeint(false);
2562
2563 EINTERLACEMETHOD method = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
2564 bool interlaced = m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_INTERLACED;
2565 m_SeenInterlaceFlag |= interlaced;
2566
2567 if (!(flags & DVD_CODEC_CTRL_NO_POSTPROC) &&
2568 interlaced &&
2569 method != VS_INTERLACEMETHOD_NONE)
2570 {
2571 if (!m_config.processInfo->Supports(method))
2572 method = VS_INTERLACEMETHOD_VDPAU_TEMPORAL;
2573
2574 if (method == VS_INTERLACEMETHOD_VDPAU_BOB ||
2575 method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL ||
2576 method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF ||
2577 method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL ||
2578 method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF)
2579 {
2580 if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF ||
2581 method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF ||
2582 !CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo())
2583 m_mixersteps = 1;
2584 else
2585 {
2586 m_mixersteps = 2;
2587 m_config.stats->SetCanSkipDeint(true);
2588 }
2589
2590 if (m_mixerInput[1].DVDPic.iFlags & DVD_CODEC_CTRL_SKIPDEINT)
2591 {
2592 m_mixersteps = 1;
2593 }
2594
2595 if(m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
2596 m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2597 else
2598 m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2599
2600 m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2601 DVP_FLAG_REPEAT_TOP_FIELD |
2602 DVP_FLAG_INTERLACED);
2603 m_mixerInput[1].isYuv = false;
2604 m_config.useInteropYuv = false;
2605 }
2606 else if (method == VS_INTERLACEMETHOD_RENDER_BOB)
2607 {
2608 m_mixersteps = 1;
2609 m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2610 m_mixerInput[1].isYuv = true;
2611 m_config.useInteropYuv = true;
2612 }
2613 }
2614 else
2615 {
2616 m_mixersteps = 1;
2617 m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
2618
2619 if (m_config.useInteropYuv)
2620 m_mixerInput[1].isYuv = true;
2621 else
2622 {
2623 m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2624 DVP_FLAG_REPEAT_TOP_FIELD |
2625 DVP_FLAG_INTERLACED);
2626 m_mixerInput[1].isYuv = false;
2627 }
2628 }
2629 m_mixerstep = 0;
2630 //m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU;
2631
2632 m_processPicture.crop = false;
2633 if (!m_mixerInput[1].isYuv)
2634 {
2635 m_processPicture.outputSurface = m_outputSurfaces.front();
2636 m_mixerInput[1].DVDPic.iWidth = m_config.outWidth;
2637 m_mixerInput[1].DVDPic.iHeight = m_config.outHeight;
2638 if (m_SeenInterlaceFlag)
2639 {
2640 double ratio = (double)m_mixerInput[1].DVDPic.iDisplayHeight / m_mixerInput[1].DVDPic.iHeight;
2641 m_mixerInput[1].DVDPic.iDisplayHeight = lrint(ratio*(m_mixerInput[1].DVDPic.iHeight-NUM_CROP_PIX*2));
2642 m_processPicture.crop = true;
2643 }
2644 }
2645 else
2646 {
2647 m_mixerInput[1].DVDPic.iWidth = m_config.vidWidth;
2648 m_mixerInput[1].DVDPic.iHeight = m_config.vidHeight;
2649 }
2650
2651 m_processPicture.isYuv = m_mixerInput[1].isYuv;
2652 m_processPicture.DVDPic.SetParams(m_mixerInput[1].DVDPic);
2653 m_processPicture.videoSurface = m_mixerInput[1].videoSurface;
2654 }
2655
FiniCycle()2656 void CMixer::FiniCycle()
2657 {
2658 // Keep video surfaces for one 2 cycles longer than used
2659 // by mixer. This avoids blocking in decoder.
2660 // NVidia recommends num_ref + 5
2661 size_t surfToKeep = 5;
2662
2663 if (m_mixerInput.size() > 0 &&
2664 (m_mixerInput[0].videoSurface == VDP_INVALID_HANDLE))
2665 surfToKeep = 1;
2666
2667 while (m_mixerInput.size() > surfToKeep)
2668 {
2669 CVdpauDecodedPicture &tmp = m_mixerInput.back();
2670 if (!m_processPicture.isYuv)
2671 {
2672 m_config.videoSurfaces->ClearRender(tmp.videoSurface);
2673 }
2674 m_mixerInput.pop_back();
2675 }
2676
2677 if (surfToKeep == 1)
2678 m_mixerInput.clear();
2679 }
2680
ProcessPicture()2681 void CMixer::ProcessPicture()
2682 {
2683 if (m_processPicture.isYuv)
2684 return;
2685
2686 VdpStatus vdp_st;
2687
2688 if (m_mixerstep == 1)
2689 {
2690 if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD)
2691 m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
2692 else
2693 m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
2694 }
2695
2696 VdpVideoSurface past_surfaces[4] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2697 VdpVideoSurface futu_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE };
2698 uint32_t pastCount = 4;
2699 uint32_t futuCount = 2;
2700
2701 if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME)
2702 {
2703 // use only 2 past 1 future for progressive/weave
2704 // (only used for postproc anyway eg noise reduction)
2705 if (m_mixerInput.size() > 3)
2706 past_surfaces[1] = m_mixerInput[3].videoSurface;
2707 if (m_mixerInput.size() > 2)
2708 past_surfaces[0] = m_mixerInput[2].videoSurface;
2709 if (m_mixerInput.size() > 1)
2710 futu_surfaces[0] = m_mixerInput[0].videoSurface;
2711 pastCount = 2;
2712 futuCount = 1;
2713 }
2714 else
2715 {
2716 if(m_mixerstep == 0)
2717 { // first field
2718 if (m_mixerInput.size() > 3)
2719 {
2720 past_surfaces[3] = m_mixerInput[3].videoSurface;
2721 past_surfaces[2] = m_mixerInput[3].videoSurface;
2722 }
2723 if (m_mixerInput.size() > 2)
2724 {
2725 past_surfaces[1] = m_mixerInput[2].videoSurface;
2726 past_surfaces[0] = m_mixerInput[2].videoSurface;
2727 }
2728 futu_surfaces[0] = m_mixerInput[1].videoSurface;
2729 futu_surfaces[1] = m_mixerInput[0].videoSurface;
2730 }
2731 else
2732 { // second field
2733 if (m_mixerInput.size() > 3)
2734 {
2735 past_surfaces[3] = m_mixerInput[3].videoSurface;
2736 }
2737 if (m_mixerInput.size() > 2)
2738 {
2739 past_surfaces[2] = m_mixerInput[2].videoSurface;
2740 past_surfaces[1] = m_mixerInput[2].videoSurface;
2741 }
2742 past_surfaces[0] = m_mixerInput[1].videoSurface;
2743 futu_surfaces[0] = m_mixerInput[0].videoSurface;
2744 futu_surfaces[1] = m_mixerInput[0].videoSurface;
2745
2746 if (m_mixerInput[0].DVDPic.pts != DVD_NOPTS_VALUE &&
2747 m_mixerInput[1].DVDPic.pts != DVD_NOPTS_VALUE)
2748 {
2749 m_processPicture.DVDPic.pts = m_mixerInput[1].DVDPic.pts +
2750 (m_mixerInput[0].DVDPic.pts -
2751 m_mixerInput[1].DVDPic.pts) / 2;
2752 }
2753 else
2754 m_processPicture.DVDPic.pts = DVD_NOPTS_VALUE;
2755 m_processPicture.DVDPic.dts = DVD_NOPTS_VALUE;
2756 }
2757 m_processPicture.DVDPic.iRepeatPicture = 0.0;
2758 } // interlaced
2759
2760 VdpRect sourceRect;
2761 sourceRect.x0 = 0;
2762 sourceRect.y0 = 0;
2763 sourceRect.x1 = m_config.vidWidth;
2764 sourceRect.y1 = m_config.vidHeight;
2765
2766 VdpRect destRect;
2767 destRect.x0 = 0;
2768 destRect.y0 = 0;
2769 destRect.x1 = m_config.outWidth;
2770 destRect.y1 = m_config.outHeight;
2771
2772 // start vdpau video mixer
2773 vdp_st = m_config.context->GetProcs().vdp_video_mixer_render(m_videoMixer,
2774 VDP_INVALID_HANDLE,
2775 0,
2776 m_mixerfield,
2777 pastCount,
2778 past_surfaces,
2779 m_mixerInput[1].videoSurface,
2780 futuCount,
2781 futu_surfaces,
2782 &sourceRect,
2783 m_processPicture.outputSurface,
2784 &destRect,
2785 &destRect,
2786 0,
2787 NULL);
2788 CheckStatus(vdp_st, __LINE__);
2789 }
2790
2791
CheckStatus(VdpStatus vdp_st,int line)2792 bool CMixer::CheckStatus(VdpStatus vdp_st, int line)
2793 {
2794 if (vdp_st != VDP_STATUS_OK)
2795 {
2796 CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d",
2797 m_config.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
2798 m_vdpError = true;
2799 return true;
2800 }
2801 return false;
2802 }
2803
2804 //-----------------------------------------------------------------------------
2805 // Output
2806 //-----------------------------------------------------------------------------
COutput(CDecoder & decoder,CEvent * inMsgEvent)2807 COutput::COutput(CDecoder &decoder, CEvent *inMsgEvent) :
2808 CThread("Vdpau Output"),
2809 m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
2810 m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
2811 m_vdpau(decoder),
2812 m_mixer(&m_outMsgEvent)
2813 {
2814 m_inMsgEvent = inMsgEvent;
2815 m_bufferPool = std::make_shared<CVdpauBufferPool>(decoder);
2816 }
2817
Start()2818 void COutput::Start()
2819 {
2820 Create();
2821 }
2822
~COutput()2823 COutput::~COutput()
2824 {
2825 Dispose();
2826 }
2827
Dispose()2828 void COutput::Dispose()
2829 {
2830 CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
2831 m_bStop = true;
2832 m_outMsgEvent.Set();
2833 StopThread();
2834 m_controlPort.Purge();
2835 m_dataPort.Purge();
2836 }
2837
OnStartup()2838 void COutput::OnStartup()
2839 {
2840 CLog::Log(LOGINFO, "COutput::OnStartup: Output Thread created");
2841 }
2842
OnExit()2843 void COutput::OnExit()
2844 {
2845 CLog::Log(LOGINFO, "COutput::OnExit: Output Thread terminated");
2846 }
2847
2848 enum OUTPUT_STATES
2849 {
2850 O_TOP = 0, // 0
2851 O_TOP_ERROR, // 1
2852 O_TOP_UNCONFIGURED, // 2
2853 O_TOP_CONFIGURED, // 3
2854 O_TOP_CONFIGURED_IDLE, // 4
2855 O_TOP_CONFIGURED_WORK, // 5
2856 };
2857
2858 int VDPAU_OUTPUT_parentStates[] = {
2859 -1,
2860 0, //TOP_ERROR
2861 0, //TOP_UNCONFIGURED
2862 0, //TOP_CONFIGURED
2863 3, //TOP_CONFIGURED_IDLE
2864 3, //TOP_CONFIGURED_WORK
2865 };
2866
StateMachine(int signal,Protocol * port,Message * msg)2867 void COutput::StateMachine(int signal, Protocol *port, Message *msg)
2868 {
2869 for (int state = m_state; ; state = VDPAU_OUTPUT_parentStates[state])
2870 {
2871 switch (state)
2872 {
2873 case O_TOP: // TOP
2874 if (port == &m_controlPort)
2875 {
2876 switch (signal)
2877 {
2878 case COutputControlProtocol::FLUSH:
2879 msg->Reply(COutputControlProtocol::ACC);
2880 return;
2881 case COutputControlProtocol::PRECLEANUP:
2882 msg->Reply(COutputControlProtocol::ACC);
2883 return;
2884 default:
2885 break;
2886 }
2887 }
2888 else if (port == &m_dataPort)
2889 {
2890 switch (signal)
2891 {
2892 case COutputDataProtocol::RETURNPIC:
2893 CVdpauRenderPicture *pic;
2894 pic = *((CVdpauRenderPicture**)msg->data);
2895 QueueReturnPicture(pic);
2896 return;
2897 default:
2898 break;
2899 }
2900 }
2901 {
2902 std::string portName = port == NULL ? "timer" : port->portName;
2903 CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
2904 }
2905 return;
2906
2907 case O_TOP_ERROR:
2908 break;
2909
2910 case O_TOP_UNCONFIGURED:
2911 if (port == &m_controlPort)
2912 {
2913 switch (signal)
2914 {
2915 case COutputControlProtocol::INIT:
2916 CVdpauConfig *data;
2917 data = (CVdpauConfig*)msg->data;
2918 if (data)
2919 {
2920 m_config = *data;
2921 }
2922 Init();
2923 Message *reply;
2924 if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::INIT,
2925 &reply, 1000, &m_config, sizeof(m_config)))
2926 {
2927 if (reply->signal != CMixerControlProtocol::ACC)
2928 m_vdpError = true;
2929 reply->Release();
2930 }
2931
2932 // set initial number of
2933 m_bufferPool->numOutputSurfaces = 4;
2934 EnsureBufferPool();
2935 if (!m_vdpError)
2936 {
2937 m_state = O_TOP_CONFIGURED_IDLE;
2938 msg->Reply(COutputControlProtocol::ACC, &m_config, sizeof(m_config));
2939 }
2940 else
2941 {
2942 m_state = O_TOP_ERROR;
2943 msg->Reply(COutputControlProtocol::ERROR);
2944 }
2945 return;
2946 default:
2947 break;
2948 }
2949 }
2950 break;
2951
2952 case O_TOP_CONFIGURED:
2953 if (port == &m_controlPort)
2954 {
2955 switch (signal)
2956 {
2957 case COutputControlProtocol::FLUSH:
2958 Flush();
2959 msg->Reply(COutputControlProtocol::ACC);
2960 return;
2961 case COutputControlProtocol::PRECLEANUP:
2962 Flush();
2963 PreCleanup();
2964 msg->Reply(COutputControlProtocol::ACC);
2965 return;
2966 default:
2967 break;
2968 }
2969 }
2970 else if (port == &m_dataPort)
2971 {
2972 switch (signal)
2973 {
2974 case COutputDataProtocol::NEWFRAME:
2975 CPayloadWrap<CVdpauDecodedPicture> *payload;
2976 payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.release());
2977 if (payload)
2978 {
2979 m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::FRAME, payload);
2980 }
2981 return;
2982 case COutputDataProtocol::RETURNPIC:
2983 CVdpauRenderPicture *pic;
2984 pic = *((CVdpauRenderPicture**)msg->data);
2985 QueueReturnPicture(pic);
2986 m_controlPort.SendInMessage(COutputControlProtocol::STATS);
2987 m_state = O_TOP_CONFIGURED_WORK;
2988 m_extTimeout = 0;
2989 return;
2990 default:
2991 break;
2992 }
2993 }
2994 else if (port == &m_mixer.m_dataPort)
2995 {
2996 switch (signal)
2997 {
2998 case CMixerDataProtocol::PICTURE:
2999 CVdpauProcessedPicture *pic;
3000 pic = (CVdpauProcessedPicture*)msg->data;
3001 m_bufferPool->processedPics.push(*pic);
3002 m_state = O_TOP_CONFIGURED_WORK;
3003 m_extTimeout = 0;
3004 return;
3005 default:
3006 break;
3007 }
3008 }
3009 break;
3010
3011 case O_TOP_CONFIGURED_IDLE:
3012 if (port == NULL) // timeout
3013 {
3014 switch (signal)
3015 {
3016 case COutputControlProtocol::TIMEOUT:
3017 m_extTimeout = 100;
3018 if (HasWork())
3019 {
3020 m_state = O_TOP_CONFIGURED_WORK;
3021 m_extTimeout = 0;
3022 }
3023 return;
3024 default:
3025 break;
3026 }
3027 }
3028 break;
3029
3030 case O_TOP_CONFIGURED_WORK:
3031 if (port == NULL) // timeout
3032 {
3033 switch (signal)
3034 {
3035 case COutputControlProtocol::TIMEOUT:
3036 if (HasWork())
3037 {
3038 CVdpauRenderPicture *pic;
3039 pic = ProcessMixerPicture();
3040 if (pic)
3041 {
3042 m_config.stats->DecProcessed();
3043 m_config.stats->IncRender();
3044 m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic));
3045 }
3046 m_extTimeout = 1;
3047 }
3048 else
3049 {
3050 m_state = O_TOP_CONFIGURED_IDLE;
3051 m_extTimeout = 0;
3052 }
3053 return;
3054 default:
3055 break;
3056 }
3057 }
3058 break;
3059
3060 default: // we are in no state, should not happen
3061 CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
3062 return;
3063 }
3064 } // for
3065 }
3066
Process()3067 void COutput::Process()
3068 {
3069 Message *msg = NULL;
3070 Protocol *port = NULL;
3071 bool gotMsg;
3072
3073 m_state = O_TOP_UNCONFIGURED;
3074 m_extTimeout = 1000;
3075 m_bStateMachineSelfTrigger = false;
3076
3077 while (!m_bStop)
3078 {
3079 gotMsg = false;
3080
3081 if (m_bStateMachineSelfTrigger)
3082 {
3083 m_bStateMachineSelfTrigger = false;
3084 // self trigger state machine
3085 StateMachine(msg->signal, port, msg);
3086 if (!m_bStateMachineSelfTrigger)
3087 {
3088 msg->Release();
3089 msg = NULL;
3090 }
3091 continue;
3092 }
3093 // check control port
3094 else if (m_controlPort.ReceiveOutMessage(&msg))
3095 {
3096 gotMsg = true;
3097 port = &m_controlPort;
3098 }
3099 // check data port
3100 else if (m_dataPort.ReceiveOutMessage(&msg))
3101 {
3102 gotMsg = true;
3103 port = &m_dataPort;
3104 }
3105 // check mixer data port
3106 else if (m_mixer.m_dataPort.ReceiveInMessage(&msg))
3107 {
3108 gotMsg = true;
3109 port = &m_mixer.m_dataPort;
3110 }
3111 if (gotMsg)
3112 {
3113 StateMachine(msg->signal, port, msg);
3114 if (!m_bStateMachineSelfTrigger)
3115 {
3116 msg->Release();
3117 msg = NULL;
3118 }
3119 continue;
3120 }
3121
3122 // wait for message
3123 else if (m_outMsgEvent.WaitMSec(m_extTimeout))
3124 {
3125 continue;
3126 }
3127 // time out
3128 else
3129 {
3130 msg = m_controlPort.GetMessage();
3131 msg->signal = COutputControlProtocol::TIMEOUT;
3132 port = 0;
3133 // signal timeout to state machine
3134 StateMachine(msg->signal, port, msg);
3135 if (!m_bStateMachineSelfTrigger)
3136 {
3137 msg->Release();
3138 msg = NULL;
3139 }
3140 }
3141 }
3142 Flush();
3143 Uninit();
3144 }
3145
Init()3146 bool COutput::Init()
3147 {
3148 m_mixer.Start();
3149 m_vdpError = false;
3150
3151 return true;
3152 }
3153
Uninit()3154 bool COutput::Uninit()
3155 {
3156 m_mixer.Dispose();
3157 ProcessSyncPicture();
3158 ReleaseBufferPool();
3159 return true;
3160 }
3161
Flush()3162 void COutput::Flush()
3163 {
3164 if (m_mixer.IsActive())
3165 {
3166 Message *reply;
3167 if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::FLUSH,
3168 &reply,
3169 2000))
3170 {
3171 reply->Release();
3172 }
3173 else
3174 CLog::Log(LOGERROR, "Coutput::%s - failed to flush mixer", __FUNCTION__);
3175 }
3176
3177 Message *msg;
3178
3179 while (m_dataPort.ReceiveInMessage(&msg))
3180 {
3181 if (msg->signal == COutputDataProtocol::PICTURE)
3182 {
3183 CVdpauRenderPicture *pic;
3184 pic = *((CVdpauRenderPicture**)msg->data);
3185 pic->Release();
3186 }
3187 msg->Release();
3188 }
3189
3190 while (m_dataPort.ReceiveOutMessage(&msg))
3191 {
3192 if (msg->signal == COutputDataProtocol::NEWFRAME)
3193 {
3194 CPayloadWrap<CVdpauDecodedPicture> *payload;
3195 payload = dynamic_cast<CPayloadWrap<CVdpauDecodedPicture>*>(msg->payloadObj.get());
3196 if (payload)
3197 {
3198 CVdpauDecodedPicture pic = *(payload->GetPlayload());
3199 m_config.videoSurfaces->ClearRender(pic.videoSurface);
3200 }
3201 }
3202 else if (msg->signal == COutputDataProtocol::RETURNPIC)
3203 {
3204 CVdpauRenderPicture *pic;
3205 pic = *((CVdpauRenderPicture**)msg->data);
3206 QueueReturnPicture(pic);
3207 }
3208 msg->Release();
3209 }
3210
3211 while (m_mixer.m_dataPort.ReceiveInMessage(&msg))
3212 {
3213 if (msg->signal == CMixerDataProtocol::PICTURE)
3214 {
3215 CVdpauProcessedPicture pic = *reinterpret_cast<CVdpauProcessedPicture*>(msg->data);
3216 m_bufferPool->processedPics.push(pic);
3217 }
3218 msg->Release();
3219 }
3220
3221 // reset used render flag which was cleared on mixer flush
3222 for (auto &awayPic : m_bufferPool->processedPicsAway)
3223 {
3224 if (awayPic.isYuv)
3225 {
3226 m_config.videoSurfaces->MarkRender(awayPic.videoSurface);
3227 }
3228 }
3229
3230 // clear processed pics
3231 while(!m_bufferPool->processedPics.empty())
3232 {
3233 CVdpauProcessedPicture procPic = m_bufferPool->processedPics.front();
3234 if (!procPic.isYuv)
3235 {
3236 m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &procPic.outputSurface, sizeof(procPic.outputSurface));
3237 }
3238 else if (procPic.isYuv)
3239 {
3240 m_config.videoSurfaces->ClearRender(procPic.videoSurface);
3241 }
3242 m_bufferPool->processedPics.pop();
3243 }
3244 }
3245
HasWork()3246 bool COutput::HasWork()
3247 {
3248 if (!m_bufferPool->processedPics.empty() && m_bufferPool->HasFree())
3249 return true;
3250 return false;
3251 }
3252
ProcessMixerPicture()3253 CVdpauRenderPicture* COutput::ProcessMixerPicture()
3254 {
3255 CVdpauRenderPicture *retPic = NULL;
3256
3257 if (!m_bufferPool->processedPics.empty() && m_bufferPool->HasFree())
3258 {
3259 retPic = m_bufferPool->GetVdpau();
3260 CVdpauProcessedPicture procPic = m_bufferPool->processedPics.front();
3261 procPic.id = m_bufferPool->procPicId++;
3262 m_bufferPool->processedPics.pop();
3263 m_bufferPool->processedPicsAway.push_back(procPic);
3264 retPic->procPic = procPic;
3265 retPic->device = reinterpret_cast<void*>(m_config.context->GetDevice());
3266 retPic->procFunc = reinterpret_cast<void*>(m_config.context->GetProcs().vdp_get_proc_address);
3267 retPic->ident = m_config.timeOpened + m_config.resetCounter;
3268
3269 retPic->DVDPic.SetParams(procPic.DVDPic);
3270 if (!procPic.isYuv)
3271 {
3272 m_config.useInteropYuv = false;
3273 m_bufferPool->numOutputSurfaces = NUM_RENDER_PICS;
3274 EnsureBufferPool();
3275 retPic->width = m_config.outWidth;
3276 retPic->height = m_config.outHeight;
3277 retPic->crop.x1 = 0;
3278 retPic->crop.y1 = procPic.crop ? NUM_CROP_PIX : 0;
3279 retPic->crop.x2 = m_config.outWidth;
3280 retPic->crop.y2 = m_config.outHeight - retPic->crop.y1;
3281 }
3282 else
3283 {
3284 m_config.useInteropYuv = true;
3285 retPic->width = m_config.surfaceWidth;
3286 retPic->height = m_config.surfaceHeight;
3287 retPic->crop.x1 = 0;
3288 retPic->crop.y1 = 0;
3289 retPic->crop.x2 = m_config.surfaceWidth - m_config.vidWidth;
3290 retPic->crop.y2 = m_config.surfaceHeight - m_config.vidHeight;
3291 }
3292 }
3293 return retPic;
3294 }
3295
QueueReturnPicture(CVdpauRenderPicture * pic)3296 void COutput::QueueReturnPicture(CVdpauRenderPicture *pic)
3297 {
3298 m_bufferPool->QueueReturnPicture(pic);
3299 ProcessSyncPicture();
3300 }
3301
ProcessSyncPicture()3302 void COutput::ProcessSyncPicture()
3303 {
3304 CVdpauRenderPicture *pic;
3305
3306 pic = m_bufferPool->ProcessSyncPicture();
3307
3308 while (pic != nullptr)
3309 {
3310 ProcessReturnPicture(pic);
3311 pic = m_bufferPool->ProcessSyncPicture();
3312 }
3313 }
3314
ProcessReturnPicture(CVdpauRenderPicture * pic)3315 void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic)
3316 {
3317 for (auto it=m_bufferPool->processedPicsAway.begin(); it!=m_bufferPool->processedPicsAway.end(); ++it)
3318 {
3319 if (it->id == pic->procPic.id)
3320 {
3321 if (pic->procPic.isYuv)
3322 {
3323 VdpVideoSurface surf = pic->procPic.videoSurface;
3324 if (surf != VDP_INVALID_HANDLE)
3325 m_config.videoSurfaces->ClearRender(surf);
3326 }
3327 else
3328 {
3329 VdpOutputSurface outSurf = pic->procPic.outputSurface;
3330 if (outSurf != VDP_INVALID_HANDLE)
3331 m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf));
3332 }
3333 m_bufferPool->processedPicsAway.erase(it);
3334 break;
3335 }
3336 }
3337 }
3338
EnsureBufferPool()3339 bool COutput::EnsureBufferPool()
3340 {
3341 VdpStatus vdp_st;
3342
3343 // Creation of outputSurfaces
3344 VdpOutputSurface outputSurface;
3345 for (int i = m_bufferPool->outputSurfaces.size(); i < m_bufferPool->numOutputSurfaces; i++)
3346 {
3347 vdp_st = m_config.context->GetProcs().vdp_output_surface_create(m_config.context->GetDevice(),
3348 VDP_RGBA_FORMAT_B8G8R8A8,
3349 m_config.outWidth,
3350 m_config.outHeight,
3351 &outputSurface);
3352 if (CheckStatus(vdp_st, __LINE__))
3353 return false;
3354 m_bufferPool->outputSurfaces.push_back(outputSurface);
3355
3356 m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3357 &outputSurface,
3358 sizeof(VdpOutputSurface));
3359 CLog::Log(LOGINFO, "VDPAU::COutput::InitBufferPool - Output Surface created");
3360 }
3361 return true;
3362 }
3363
ReleaseBufferPool()3364 void COutput::ReleaseBufferPool()
3365 {
3366 VdpStatus vdp_st;
3367
3368 // release all output surfaces
3369 m_bufferPool->InvalidateUsed();
3370 for (unsigned int i = 0; i < m_bufferPool->outputSurfaces.size(); ++i)
3371 {
3372 if (m_bufferPool->outputSurfaces[i] == VDP_INVALID_HANDLE)
3373 continue;
3374 vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool->outputSurfaces[i]);
3375 CheckStatus(vdp_st, __LINE__);
3376 }
3377 m_bufferPool->outputSurfaces.clear();
3378
3379 ProcessSyncPicture();
3380 }
3381
PreCleanup()3382 void COutput::PreCleanup()
3383 {
3384
3385 VdpStatus vdp_st;
3386
3387 m_mixer.Dispose();
3388 ProcessSyncPicture();
3389
3390 for (unsigned int i = 0; i < m_bufferPool->outputSurfaces.size(); ++i)
3391 {
3392 if (m_bufferPool->outputSurfaces[i] == VDP_INVALID_HANDLE)
3393 continue;
3394
3395 // check if output surface is in use
3396 bool used = false;
3397 for (auto &picAway : m_bufferPool->processedPicsAway)
3398 {
3399 if (picAway.outputSurface == m_bufferPool->outputSurfaces[i])
3400 {
3401 used = true;
3402 break;
3403 }
3404 }
3405 if (used)
3406 continue;
3407
3408 vdp_st = m_config.context->GetProcs().vdp_output_surface_destroy(m_bufferPool->outputSurfaces[i]);
3409 CheckStatus(vdp_st, __LINE__);
3410
3411 m_bufferPool->outputSurfaces[i] = VDP_INVALID_HANDLE;
3412 CLog::Log(LOGDEBUG, LOGVIDEO, "VDPAU::PreCleanup - released output surface");
3413 }
3414
3415 }
3416
InitMixer()3417 void COutput::InitMixer()
3418 {
3419 for (unsigned int i = 0; i < m_bufferPool->outputSurfaces.size(); ++i)
3420 {
3421 m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER,
3422 &m_bufferPool->outputSurfaces[i],
3423 sizeof(VdpOutputSurface));
3424 }
3425 }
3426
CheckStatus(VdpStatus vdp_st,int line)3427 bool COutput::CheckStatus(VdpStatus vdp_st, int line)
3428 {
3429 if (vdp_st != VDP_STATUS_OK)
3430 {
3431 CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d",
3432 m_config.context->GetProcs().vdp_get_error_string(vdp_st), vdp_st, __FILE__, line);
3433 m_vdpError = true;
3434 return true;
3435 }
3436 return false;
3437 }
3438