1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "VAAPI.h"
10
11 #include "DVDVideoCodec.h"
12 #include "ServiceBroker.h"
13 #include "cores/VideoPlayer/DVDCodecs/DVDCodecUtils.h"
14 #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
15 #include "cores/VideoPlayer/Interface/TimingConstants.h"
16 #include "cores/VideoPlayer/Process/ProcessInfo.h"
17 #include "settings/AdvancedSettings.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "settings/lib/Setting.h"
21 #include "threads/SingleLock.h"
22 #include "utils/MemUtils.h"
23 #include "utils/StringUtils.h"
24 #include "utils/XTimeUtils.h"
25 #include "utils/log.h"
26 #include "windowing/GraphicContext.h"
27
28 #include <drm_fourcc.h>
29 #include <va/va_drm.h>
30 #include <va/va_drmcommon.h>
31
32 extern "C" {
33 #include <libavutil/avutil.h>
34 #include <libavutil/hwcontext.h>
35 #include <libavutil/hwcontext_vaapi.h>
36 #include <libavutil/opt.h>
37 #include <libavfilter/buffersink.h>
38 #include <libavfilter/buffersrc.h>
39 }
40
41 #include "system_egl.h"
42
43 #include <EGL/eglext.h>
44 #include <va/va_vpp.h>
45 #include <xf86drm.h>
46
47 #if VA_CHECK_VERSION(1, 0, 0)
48 # include <va/va_str.h>
49 #endif
50
51 using namespace VAAPI;
52 #define NUM_RENDER_PICS 7
53
54 constexpr auto SETTING_VIDEOPLAYER_USEVAAPI = "videoplayer.usevaapi";
55 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIHEVC = "videoplayer.usevaapihevc";
56 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIMPEG2 = "videoplayer.usevaapimpeg2";
57 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIMPEG4 = "videoplayer.usevaapimpeg4";
58 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIVC1 = "videoplayer.usevaapivc1";
59 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIVP8 = "videoplayer.usevaapivp8";
60 constexpr auto SETTING_VIDEOPLAYER_USEVAAPIVP9 = "videoplayer.usevaapivp9";
61 constexpr auto SETTING_VIDEOPLAYER_PREFERVAAPIRENDER = "videoplayer.prefervaapirender";
62
VaErrorCallback(void * user_context,const char * message)63 void VAAPI::VaErrorCallback(void *user_context, const char *message)
64 {
65 std::string str{message};
66 CLog::Log(LOGERROR, "libva error: {}", StringUtils::TrimRight(str));
67 }
68
VaInfoCallback(void * user_context,const char * message)69 void VAAPI::VaInfoCallback(void *user_context, const char *message)
70 {
71 std::string str{message};
72 CLog::Log(LOGDEBUG, "libva info: {}", StringUtils::TrimRight(str));
73 }
74
75 //-----------------------------------------------------------------------------
76 //-----------------------------------------------------------------------------
77
78 CVAAPIContext *CVAAPIContext::m_context = 0;
79 CCriticalSection CVAAPIContext::m_section;
80
CVAAPIContext()81 CVAAPIContext::CVAAPIContext()
82 {
83 m_context = 0;
84 m_refCount = 0;
85 m_profiles = NULL;
86 m_display = NULL;
87 }
88
Release(CDecoder * decoder)89 void CVAAPIContext::Release(CDecoder *decoder)
90 {
91 CSingleLock lock(m_section);
92
93 auto it = find(m_decoders.begin(), m_decoders.end(), decoder);
94 if (it != m_decoders.end())
95 m_decoders.erase(it);
96
97 m_refCount--;
98 if (m_refCount <= 0)
99 {
100 Close();
101 delete this;
102 m_context = 0;
103 }
104 }
105
Close()106 void CVAAPIContext::Close()
107 {
108 CLog::Log(LOGINFO, "VAAPI::Close - closing decoder context");
109 if (m_renderNodeFD >= 0)
110 {
111 close(m_renderNodeFD);
112 }
113
114 DestroyContext();
115 }
116
EnsureContext(CVAAPIContext ** ctx,CDecoder * decoder)117 bool CVAAPIContext::EnsureContext(CVAAPIContext **ctx, CDecoder *decoder)
118 {
119 CSingleLock lock(m_section);
120
121 if (m_context)
122 {
123 m_context->m_refCount++;
124 *ctx = m_context;
125 if (!m_context->IsValidDecoder(decoder))
126 m_context->m_decoders.push_back(decoder);
127 return true;
128 }
129
130 m_context = new CVAAPIContext();
131 *ctx = m_context;
132 {
133 CSingleLock gLock(CServiceBroker::GetWinSystem()->GetGfxContext());
134 if (!m_context->CreateContext())
135 {
136 delete m_context;
137 m_context = 0;
138 *ctx = NULL;
139 return false;
140 }
141 }
142
143 m_context->m_refCount++;
144
145 if (!m_context->IsValidDecoder(decoder))
146 m_context->m_decoders.push_back(decoder);
147 *ctx = m_context;
148 return true;
149 }
150
SetValidDRMVaDisplayFromRenderNode()151 void CVAAPIContext::SetValidDRMVaDisplayFromRenderNode()
152 {
153 int const buf_size{128};
154 char name[buf_size];
155 int fd{-1};
156
157 // 128 is the start of the NUM in renderD<NUM>
158 for (int i = 128; i < (128 + 16); i++)
159 {
160 snprintf(name, buf_size, "/dev/dri/renderD%u", i);
161
162 fd = open(name, O_RDWR);
163
164 if (fd < 0)
165 {
166 continue;
167 }
168
169 auto display = vaGetDisplayDRM(fd);
170
171 if (display != nullptr)
172 {
173 m_renderNodeFD = fd;
174 m_display = display;
175 return;
176 }
177 close(fd);
178 }
179
180 CLog::Log(LOGERROR, "Failed to find any open render nodes in /dev/dri/renderD<num>");
181 }
182
SetVaDisplayForSystem()183 void CVAAPIContext::SetVaDisplayForSystem()
184 {
185 m_display = CDecoder::m_pWinSystem->GetVADisplay();
186
187 // Fallback to DRM
188 if (!m_display)
189 {
190 // Render nodes depends on kernel >= 3.15
191 SetValidDRMVaDisplayFromRenderNode();
192 }
193 }
194
CreateContext()195 bool CVAAPIContext::CreateContext()
196 {
197 SetVaDisplayForSystem();
198
199 if (m_display == nullptr)
200 {
201 CLog::Log(LOGERROR, "Failed to find any VaDisplays for this system");
202 return false;
203 }
204
205 #if VA_CHECK_VERSION(1, 0, 0)
206 vaSetErrorCallback(m_display, VaErrorCallback, nullptr);
207 vaSetInfoCallback(m_display, VaInfoCallback, nullptr);
208 #endif
209
210 int major_version, minor_version;
211 if (!CheckSuccess(vaInitialize(m_display, &major_version, &minor_version), "vaInitialize"))
212 {
213 vaTerminate(m_display);
214 m_display = NULL;
215 return false;
216 }
217
218 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - initialize version %d.%d", major_version, minor_version);
219 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - driver in use: %s", vaQueryVendorString(m_display));
220
221 QueryCaps();
222 if (!m_profileCount)
223 return false;
224
225 return true;
226 }
227
DestroyContext()228 void CVAAPIContext::DestroyContext()
229 {
230 delete[] m_profiles;
231 if (m_display)
232 {
233 if (CheckSuccess(vaTerminate(m_display), "vaTerminate"))
234 {
235 m_display = NULL;
236 }
237 else
238 {
239 #if VA_CHECK_VERSION(1, 0, 0)
240 vaSetErrorCallback(m_display, nullptr, nullptr);
241 vaSetInfoCallback(m_display, nullptr, nullptr);
242 #endif
243 }
244 }
245 }
246
QueryCaps()247 void CVAAPIContext::QueryCaps()
248 {
249 m_profileCount = 0;
250
251 int max_profiles = vaMaxNumProfiles(m_display);
252 m_profiles = new VAProfile[max_profiles];
253
254 if (!CheckSuccess(vaQueryConfigProfiles(m_display, m_profiles, &m_profileCount), "vaQueryConfigProfiles"))
255 return;
256
257 for(int i = 0; i < m_profileCount; i++)
258 {
259 #if VA_CHECK_VERSION(1, 0, 0)
260 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - profile %s", vaProfileStr(m_profiles[i]));
261 #else
262 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - profile %d", m_profiles[i]);
263 #endif
264 }
265 }
266
GetAttrib(VAProfile profile)267 VAConfigAttrib CVAAPIContext::GetAttrib(VAProfile profile)
268 {
269 CSingleLock lock(m_section);
270
271 VAConfigAttrib attrib;
272 attrib.type = VAConfigAttribRTFormat;
273 CheckSuccess(vaGetConfigAttributes(m_display, profile, VAEntrypointVLD, &attrib, 1), "vaGetConfigAttributes");
274
275 return attrib;
276 }
277
SupportsProfile(VAProfile profile)278 bool CVAAPIContext::SupportsProfile(VAProfile profile)
279 {
280 CSingleLock lock(m_section);
281
282 for (int i=0; i<m_profileCount; i++)
283 {
284 if (m_profiles[i] == profile)
285 return true;
286 }
287 return false;
288 }
289
CreateConfig(VAProfile profile,VAConfigAttrib attrib)290 VAConfigID CVAAPIContext::CreateConfig(VAProfile profile, VAConfigAttrib attrib)
291 {
292 CSingleLock lock(m_section);
293
294 VAConfigID config = VA_INVALID_ID;
295 CheckSuccess(vaCreateConfig(m_display, profile, VAEntrypointVLD, &attrib, 1, &config), "vaCreateConfig");
296
297 return config;
298 }
299
CheckSuccess(VAStatus status,const std::string & function)300 bool CVAAPIContext::CheckSuccess(VAStatus status, const std::string& function)
301 {
302 if (status != VA_STATUS_SUCCESS)
303 {
304 CLog::Log(LOGERROR, "VAAPI/context {} error: {} ({})", function, vaErrorStr(status), status);
305 return false;
306 }
307 return true;
308 }
309
GetDisplay()310 VADisplay CVAAPIContext::GetDisplay()
311 {
312 return m_display;
313 }
314
IsValidDecoder(CDecoder * decoder)315 bool CVAAPIContext::IsValidDecoder(CDecoder *decoder)
316 {
317 auto it = find(m_decoders.begin(), m_decoders.end(), decoder);
318 if (it != m_decoders.end())
319 return true;
320
321 return false;
322 }
323
FFReleaseBuffer(void * opaque,uint8_t * data)324 void CVAAPIContext::FFReleaseBuffer(void *opaque, uint8_t *data)
325 {
326 CDecoder *va = static_cast<CDecoder*>(opaque);
327 if (m_context && m_context->IsValidDecoder(va))
328 {
329 va->FFReleaseBuffer(data);
330 }
331 }
332
333 //-----------------------------------------------------------------------------
334 // VAAPI Video Surface states
335 //-----------------------------------------------------------------------------
336
337 #define SURFACE_USED_FOR_REFERENCE 0x01
338 #define SURFACE_USED_FOR_RENDER 0x02
339
AddSurface(VASurfaceID surf)340 void CVideoSurfaces::AddSurface(VASurfaceID surf)
341 {
342 CSingleLock lock(m_section);
343 m_state[surf] = 0;
344 m_freeSurfaces.push_back(surf);
345 }
346
ClearReference(VASurfaceID surf)347 void CVideoSurfaces::ClearReference(VASurfaceID surf)
348 {
349 CSingleLock lock(m_section);
350 if (m_state.find(surf) == m_state.end())
351 {
352 CLog::Log(LOGWARNING, "CVideoSurfaces::ClearReference - surface invalid");
353 return;
354 }
355 m_state[surf] &= ~SURFACE_USED_FOR_REFERENCE;
356 if (m_state[surf] == 0)
357 {
358 m_freeSurfaces.push_back(surf);
359 }
360 }
361
MarkRender(VASurfaceID surf)362 bool CVideoSurfaces::MarkRender(VASurfaceID surf)
363 {
364 CSingleLock lock(m_section);
365 if (m_state.find(surf) == m_state.end())
366 {
367 CLog::Log(LOGWARNING, "CVideoSurfaces::MarkRender - surface invalid");
368 return false;
369 }
370 auto it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
371 if (it != m_freeSurfaces.end())
372 {
373 m_freeSurfaces.erase(it);
374 }
375 m_state[surf] |= SURFACE_USED_FOR_RENDER;
376 return true;
377 }
378
ClearRender(VASurfaceID surf)379 void CVideoSurfaces::ClearRender(VASurfaceID surf)
380 {
381 CSingleLock lock(m_section);
382 if (m_state.find(surf) == m_state.end())
383 {
384 CLog::Log(LOGWARNING, "CVideoSurfaces::ClearRender - surface invalid");
385 return;
386 }
387 m_state[surf] &= ~SURFACE_USED_FOR_RENDER;
388 if (m_state[surf] == 0)
389 {
390 m_freeSurfaces.push_back(surf);
391 }
392 }
393
IsValid(VASurfaceID surf)394 bool CVideoSurfaces::IsValid(VASurfaceID surf)
395 {
396 CSingleLock lock(m_section);
397 if (m_state.find(surf) != m_state.end())
398 return true;
399 else
400 return false;
401 }
402
GetFree(VASurfaceID surf)403 VASurfaceID CVideoSurfaces::GetFree(VASurfaceID surf)
404 {
405 CSingleLock lock(m_section);
406 if (m_state.find(surf) != m_state.end())
407 {
408 auto it = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
409 if (it == m_freeSurfaces.end())
410 {
411 CLog::Log(LOGWARNING, "CVideoSurfaces::GetFree - surface not free");
412 }
413 else
414 {
415 m_freeSurfaces.erase(it);
416 m_state[surf] = SURFACE_USED_FOR_REFERENCE;
417 return surf;
418 }
419 }
420
421 if (!m_freeSurfaces.empty())
422 {
423 VASurfaceID freeSurf = m_freeSurfaces.front();
424 m_freeSurfaces.pop_front();
425 m_state[freeSurf] = SURFACE_USED_FOR_REFERENCE;
426 return freeSurf;
427 }
428
429 return VA_INVALID_SURFACE;
430 }
431
GetAtIndex(int idx)432 VASurfaceID CVideoSurfaces::GetAtIndex(int idx)
433 {
434 if ((size_t) idx >= m_state.size())
435 return VA_INVALID_SURFACE;
436
437 auto it = m_state.begin();
438 for(int i = 0; i < idx; i++)
439 ++it;
440 return it->first;
441 }
442
RemoveNext(bool skiprender)443 VASurfaceID CVideoSurfaces::RemoveNext(bool skiprender)
444 {
445 CSingleLock lock(m_section);
446 VASurfaceID surf;
447 for(auto it = m_state.begin(); it != m_state.end(); ++it)
448 {
449 if (skiprender && it->second & SURFACE_USED_FOR_RENDER)
450 continue;
451 surf = it->first;
452 m_state.erase(surf);
453
454 auto it2 = std::find(m_freeSurfaces.begin(), m_freeSurfaces.end(), surf);
455 if (it2 != m_freeSurfaces.end())
456 m_freeSurfaces.erase(it2);
457 return surf;
458 }
459 return VA_INVALID_SURFACE;
460 }
461
Reset()462 void CVideoSurfaces::Reset()
463 {
464 CSingleLock lock(m_section);
465 m_freeSurfaces.clear();
466 m_state.clear();
467 }
468
Size()469 int CVideoSurfaces::Size()
470 {
471 CSingleLock lock(m_section);
472 return m_state.size();
473 }
474
HasFree()475 bool CVideoSurfaces::HasFree()
476 {
477 CSingleLock lock(m_section);
478 return !m_freeSurfaces.empty();
479 }
480
NumFree()481 int CVideoSurfaces::NumFree()
482 {
483 CSingleLock lock(m_section);
484 return m_freeSurfaces.size();
485 }
486
HasRefs()487 bool CVideoSurfaces::HasRefs()
488 {
489 CSingleLock lock(m_section);
490 for (const auto &i : m_state)
491 {
492 if (i.second & SURFACE_USED_FOR_REFERENCE)
493 return true;
494 }
495 return false;
496 }
497
498 //-----------------------------------------------------------------------------
499 // VAAPI
500 //-----------------------------------------------------------------------------
501
502 bool CDecoder::m_capGeneral = false;
503 bool CDecoder::m_capDeepColor = false;
504 IVaapiWinSystem* CDecoder::m_pWinSystem = nullptr;
505
CDecoder(CProcessInfo & processInfo)506 CDecoder::CDecoder(CProcessInfo& processInfo) :
507 m_vaapiOutput(*this, &m_inMsgEvent),
508 m_processInfo(processInfo)
509 {
510 m_vaapiConfig.videoSurfaces = &m_videoSurfaces;
511
512 m_vaapiConfigured = false;
513 m_DisplayState = VAAPI_OPEN;
514 m_vaapiConfig.context = 0;
515 m_vaapiConfig.configId = VA_INVALID_ID;
516 m_vaapiConfig.processInfo = &m_processInfo;
517 m_avctx = nullptr;
518 m_getBufferError = 0;
519 }
520
~CDecoder()521 CDecoder::~CDecoder()
522 {
523 Close();
524 }
525
Open(AVCodecContext * avctx,AVCodecContext * mainctx,const enum AVPixelFormat fmt)526 bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, const enum AVPixelFormat fmt)
527 {
528 if (!m_capGeneral)
529 return false;
530
531 // check if user wants to decode this format with VAAPI
532 std::map<AVCodecID, std::string> settings_map = {
533 { AV_CODEC_ID_H263, SETTING_VIDEOPLAYER_USEVAAPIMPEG4 },
534 { AV_CODEC_ID_MPEG4, SETTING_VIDEOPLAYER_USEVAAPIMPEG4 },
535 { AV_CODEC_ID_WMV3, SETTING_VIDEOPLAYER_USEVAAPIVC1 },
536 { AV_CODEC_ID_VC1, SETTING_VIDEOPLAYER_USEVAAPIVC1 },
537 { AV_CODEC_ID_MPEG2VIDEO, SETTING_VIDEOPLAYER_USEVAAPIMPEG2 },
538 { AV_CODEC_ID_VP8, SETTING_VIDEOPLAYER_USEVAAPIVP8 },
539 { AV_CODEC_ID_VP9, SETTING_VIDEOPLAYER_USEVAAPIVP9 },
540 { AV_CODEC_ID_HEVC, SETTING_VIDEOPLAYER_USEVAAPIHEVC },
541 };
542
543 auto entry = settings_map.find(avctx->codec_id);
544 if (entry != settings_map.end())
545 {
546 auto settingsComponent = CServiceBroker::GetSettingsComponent();
547 if (!settingsComponent)
548 return false;
549
550 auto settings = settingsComponent->GetSettings();
551 if (!settings)
552 return false;
553
554 auto setting = settings->GetSetting(entry->second);
555 if (!setting)
556 {
557 CLog::Log(LOGERROR, "Failed to load setting for: {}", entry->second);
558 return false;
559 }
560
561 bool enabled = settings->GetBool(entry->second) && setting->IsVisible();
562 if (!enabled)
563 return false;
564 }
565
566 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI - open decoder");
567
568 if (!CVAAPIContext::EnsureContext(&m_vaapiConfig.context, this))
569 return false;
570
571 if(avctx->coded_width == 0 ||
572 avctx->coded_height == 0)
573 {
574 CLog::Log(LOGWARNING,"VAAPI::Open: no width/height available, can't init");
575 return false;
576 }
577
578 m_vaapiConfig.driverIsMesa = StringUtils::StartsWith(vaQueryVendorString(m_vaapiConfig.context->GetDisplay()), "Mesa");
579 m_vaapiConfig.vidWidth = avctx->width;
580 m_vaapiConfig.vidHeight = avctx->height;
581 m_vaapiConfig.outWidth = avctx->width;
582 m_vaapiConfig.outHeight = avctx->height;
583 m_vaapiConfig.surfaceWidth = avctx->coded_width;
584 m_vaapiConfig.surfaceHeight = avctx->coded_height;
585 m_vaapiConfig.aspect = avctx->sample_aspect_ratio;
586 m_DisplayState = VAAPI_OPEN;
587 m_vaapiConfigured = false;
588 m_presentPicture = nullptr;
589 m_getBufferError = 0;
590
591 VAProfile profile;
592 switch (avctx->codec_id)
593 {
594 case AV_CODEC_ID_MPEG2VIDEO:
595 profile = VAProfileMPEG2Main;
596 if (!m_vaapiConfig.context->SupportsProfile(profile))
597 return false;
598 break;
599 case AV_CODEC_ID_MPEG4:
600 case AV_CODEC_ID_H263:
601 profile = VAProfileMPEG4AdvancedSimple;
602 if (!m_vaapiConfig.context->SupportsProfile(profile))
603 return false;
604 break;
605 case AV_CODEC_ID_H264:
606 {
607 if (avctx->profile == FF_PROFILE_H264_CONSTRAINED_BASELINE)
608 {
609 profile = VAProfileH264ConstrainedBaseline;
610 if (!m_vaapiConfig.context->SupportsProfile(profile))
611 return false;
612 }
613 else
614 {
615 if(avctx->profile == FF_PROFILE_H264_MAIN)
616 {
617 profile = VAProfileH264Main;
618 if (m_vaapiConfig.context->SupportsProfile(profile))
619 break;
620 }
621 profile = VAProfileH264High;
622 if (!m_vaapiConfig.context->SupportsProfile(profile))
623 return false;
624 }
625 break;
626 }
627 case AV_CODEC_ID_HEVC:
628 {
629 if (avctx->profile == FF_PROFILE_HEVC_MAIN_10)
630 {
631 if (!m_capDeepColor)
632 return false;
633
634 profile = VAProfileHEVCMain10;
635 }
636 else if (avctx->profile == FF_PROFILE_HEVC_MAIN)
637 profile = VAProfileHEVCMain;
638 else
639 profile = VAProfileNone;
640 if (!m_vaapiConfig.context->SupportsProfile(profile))
641 return false;
642 break;
643 }
644 case AV_CODEC_ID_VP8:
645 {
646 profile = VAProfileVP8Version0_3;
647 if (!m_vaapiConfig.context->SupportsProfile(profile))
648 return false;
649 break;
650 }
651 case AV_CODEC_ID_VP9:
652 {
653 if (avctx->profile == FF_PROFILE_VP9_0)
654 profile = VAProfileVP9Profile0;
655 else if (avctx->profile == FF_PROFILE_VP9_2)
656 profile = VAProfileVP9Profile2;
657 else
658 profile = VAProfileNone;
659 if (!m_vaapiConfig.context->SupportsProfile(profile))
660 return false;
661 break;
662 }
663 case AV_CODEC_ID_WMV3:
664 profile = VAProfileVC1Main;
665 if (!m_vaapiConfig.context->SupportsProfile(profile))
666 return false;
667 break;
668 case AV_CODEC_ID_VC1:
669 profile = VAProfileVC1Advanced;
670 if (!m_vaapiConfig.context->SupportsProfile(profile))
671 return false;
672 break;
673 default:
674 return false;
675 }
676
677 m_vaapiConfig.profile = profile;
678 m_vaapiConfig.attrib = m_vaapiConfig.context->GetAttrib(profile);
679 if ((m_vaapiConfig.attrib.value & (VA_RT_FORMAT_YUV420 | VA_RT_FORMAT_YUV420_10BPP)) == 0)
680 {
681 CLog::Log(LOGERROR, "VAAPI - invalid yuv format %x", m_vaapiConfig.attrib.value);
682 return false;
683 }
684
685 if (avctx->codec_id == AV_CODEC_ID_H264)
686 {
687 m_vaapiConfig.maxReferences = avctx->refs;
688 if (m_vaapiConfig.maxReferences > 16)
689 m_vaapiConfig.maxReferences = 16;
690 if (m_vaapiConfig.maxReferences < 5)
691 m_vaapiConfig.maxReferences = 5;
692 }
693 else if (avctx->codec_id == AV_CODEC_ID_HEVC)
694 m_vaapiConfig.maxReferences = 16;
695 else if (avctx->codec_id == AV_CODEC_ID_VP9)
696 m_vaapiConfig.maxReferences = 8;
697 else
698 m_vaapiConfig.maxReferences = 2;
699
700 // add an extra surface for safety, some faulty material
701 // make ffmpeg require more buffers
702 m_vaapiConfig.maxReferences += 6;
703
704 if (!ConfigVAAPI())
705 {
706 return false;
707 }
708
709 AVBufferRef *deviceRef = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
710 AVHWDeviceContext *deviceCtx = (AVHWDeviceContext*)deviceRef->data;
711 AVVAAPIDeviceContext *vaapiDeviceCtx = (AVVAAPIDeviceContext*)deviceCtx->hwctx;
712 AVBufferRef *framesRef = av_hwframe_ctx_alloc(deviceRef);
713 AVHWFramesContext *framesCtx = (AVHWFramesContext*)framesRef->data;
714 AVVAAPIFramesContext *vaapiFramesCtx = (AVVAAPIFramesContext*)framesCtx->hwctx;
715
716 vaapiDeviceCtx->display = m_vaapiConfig.dpy;
717 vaapiDeviceCtx->driver_quirks = AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS;
718 vaapiFramesCtx->nb_attributes = 0;
719 vaapiFramesCtx->nb_surfaces = m_videoSurfaces.Size();
720 VASurfaceID *surfaceIds = (VASurfaceID*)av_malloc(vaapiFramesCtx->nb_surfaces * sizeof(VASurfaceID));
721 for (int i=0; i<vaapiFramesCtx->nb_surfaces; ++i)
722 surfaceIds[i] = m_videoSurfaces.GetAtIndex(i);
723 vaapiFramesCtx->surface_ids = surfaceIds;
724 framesCtx->format = AV_PIX_FMT_VAAPI;
725 framesCtx->width = avctx->coded_width;
726 framesCtx->height = avctx->coded_height;
727
728 avctx->hw_frames_ctx = framesRef;
729 avctx->get_buffer2 = CDecoder::FFGetBuffer;
730 avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
731
732 m_avctx = mainctx;
733 return true;
734 }
735
Close()736 void CDecoder::Close()
737 {
738 CLog::Log(LOGINFO, "VAAPI::%s", __FUNCTION__);
739
740 CSingleLock lock(m_DecoderSection);
741
742 FiniVAAPIOutput();
743
744 if (m_vaapiConfig.context)
745 m_vaapiConfig.context->Release(this);
746 m_vaapiConfig.context = 0;
747 }
748
Release()749 long CDecoder::Release()
750 {
751 // if ffmpeg holds any references, flush buffers
752 if (m_avctx && m_videoSurfaces.HasRefs())
753 {
754 avcodec_flush_buffers(m_avctx);
755 }
756
757 if (m_presentPicture)
758 {
759 m_presentPicture->Release();
760 m_presentPicture = nullptr;
761 }
762 // check if we should do some pre-cleanup here
763 // a second decoder might need resources
764 if (m_vaapiConfigured == true)
765 {
766 CSingleLock lock(m_DecoderSection);
767 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::Release pre-cleanup");
768
769 CSingleLock lock1(CServiceBroker::GetWinSystem()->GetGfxContext());
770 Message *reply;
771 if (m_vaapiOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP,
772 &reply,
773 2000))
774 {
775 bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
776 reply->Release();
777 if (!success)
778 {
779 CLog::Log(LOGERROR, "VAAPI::%s - pre-cleanup returned error", __FUNCTION__);
780 m_DisplayState = VAAPI_ERROR;
781 }
782 }
783 else
784 {
785 CLog::Log(LOGERROR, "VAAPI::%s - pre-cleanup timed out", __FUNCTION__);
786 m_DisplayState = VAAPI_ERROR;
787 }
788
789 VASurfaceID surf;
790 while((surf = m_videoSurfaces.RemoveNext(true)) != VA_INVALID_SURFACE)
791 {
792 CheckSuccess(vaDestroySurfaces(m_vaapiConfig.dpy, &surf, 1), "vaDestroySurfaces");
793 }
794 }
795 return IHardwareDecoder::Release();
796 }
797
ReleasePicReference()798 long CDecoder::ReleasePicReference()
799 {
800 return IHardwareDecoder::Release();
801 }
802
FFGetBuffer(AVCodecContext * avctx,AVFrame * pic,int flags)803 int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags)
804 {
805 ICallbackHWAccel* cb = static_cast<ICallbackHWAccel*>(avctx->opaque);
806 CDecoder* va = static_cast<CDecoder*>(cb->GetHWAccel());
807
808 // while we are waiting to recover we can't do anything
809 CSingleLock lock(va->m_DecoderSection);
810
811 if(va->m_DisplayState != VAAPI_OPEN)
812 {
813 CLog::Log(LOGWARNING, "VAAPI::FFGetBuffer - returning due to awaiting recovery");
814 return -1;
815 }
816
817 VASurfaceID surf = (VASurfaceID)(uintptr_t)pic->data[3];
818 surf = va->m_videoSurfaces.GetFree(surf != 0 ? surf : VA_INVALID_SURFACE);
819
820 if (surf == VA_INVALID_SURFACE)
821 {
822 uint16_t decoded, processed, render;
823 bool vpp;
824 va->m_bufferStats.Get(decoded, processed, render, vpp);
825 CLog::Log(LOGWARNING, "VAAPI::FFGetBuffer - no surface available - dec: %d, render: %d",
826 decoded, render);
827 va->m_getBufferError++;
828 return -1;
829 }
830
831 va->m_getBufferError = 0;
832
833 pic->data[1] = pic->data[2] = NULL;
834 pic->data[0] = (uint8_t*)(uintptr_t)surf;
835 pic->data[3] = (uint8_t*)(uintptr_t)surf;
836 pic->linesize[0] = pic->linesize[1] = pic->linesize[2] = 0;
837 AVBufferRef *buffer = av_buffer_create(pic->data[3], 0, CVAAPIContext::FFReleaseBuffer, va, 0);
838 if (!buffer)
839 {
840 CLog::Log(LOGERROR, "VAAPI::%s - error creating buffer", __FUNCTION__);
841 return -1;
842 }
843 pic->buf[0] = buffer;
844
845 pic->reordered_opaque = avctx->reordered_opaque;
846 va->Acquire();
847 return 0;
848 }
849
FFReleaseBuffer(uint8_t * data)850 void CDecoder::FFReleaseBuffer(uint8_t *data)
851 {
852 {
853 VASurfaceID surf;
854
855 CSingleLock lock(m_DecoderSection);
856
857 surf = (VASurfaceID)(uintptr_t)data;
858 m_videoSurfaces.ClearReference(surf);
859 }
860
861 IHardwareDecoder::Release();
862 }
863
SetCodecControl(int flags)864 void CDecoder::SetCodecControl(int flags)
865 {
866 m_codecControl = flags & (DVD_CODEC_CTRL_DRAIN | DVD_CODEC_CTRL_HURRY);
867 }
868
Decode(AVCodecContext * avctx,AVFrame * pFrame)869 CDVDVideoCodec::VCReturn CDecoder::Decode(AVCodecContext* avctx, AVFrame* pFrame)
870 {
871 CDVDVideoCodec::VCReturn result = Check(avctx);
872 if (result != CDVDVideoCodec::VC_NOBUFFER && result != CDVDVideoCodec::VC_NONE)
873 return result;
874
875 CSingleLock lock(m_DecoderSection);
876
877 if (!m_vaapiConfigured)
878 return CDVDVideoCodec::VC_ERROR;
879
880 if (pFrame)
881 { // we have a new frame from decoder
882
883 VASurfaceID surf = (VASurfaceID)(uintptr_t)pFrame->data[3];
884 // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid
885 if (!m_videoSurfaces.IsValid(surf))
886 {
887 CLog::Log(LOGWARNING, "VAAPI::Decode - ignoring invalid buffer");
888 return CDVDVideoCodec::VC_BUFFER;
889 }
890 m_videoSurfaces.MarkRender(surf);
891
892 // send frame to output for processing
893 CVaapiDecodedPicture *pic = new CVaapiDecodedPicture();
894 static_cast<ICallbackHWAccel*>(avctx->opaque)->GetPictureCommon(&(pic->DVDPic));
895 m_codecControl = pic->DVDPic.iFlags & (DVD_CODEC_CTRL_HURRY | DVD_CODEC_CTRL_NO_POSTPROC);
896 pic->videoSurface = surf;
897 m_bufferStats.IncDecoded();
898 CPayloadWrap<CVaapiDecodedPicture> *payload = new CPayloadWrap<CVaapiDecodedPicture>(pic);
899 m_vaapiOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, payload);
900 }
901
902 uint16_t decoded, processed, render;
903 bool vpp;
904 Message *msg;
905 while (m_vaapiOutput.m_controlPort.ReceiveInMessage(&msg))
906 {
907 if (msg->signal == COutputControlProtocol::ERROR)
908 {
909 m_DisplayState = VAAPI_ERROR;
910 msg->Release();
911 return CDVDVideoCodec::VC_ERROR;
912 }
913 msg->Release();
914 }
915
916 bool drain = (m_codecControl & DVD_CODEC_CTRL_DRAIN);
917
918 m_bufferStats.Get(decoded, processed, render, vpp);
919 // if all pics are drained, break the loop by setting VC_EOF
920 if (drain && decoded <= 0 && processed <= 0 && render <= 0)
921 return CDVDVideoCodec::VC_EOF;
922
923 while (true)
924 {
925 // first fill the buffers to keep vaapi busy
926 if (!drain && decoded < 2 && processed < 3 && m_videoSurfaces.HasFree())
927 {
928 return CDVDVideoCodec::VC_BUFFER;
929 }
930 else if (m_vaapiOutput.m_dataPort.ReceiveInMessage(&msg))
931 {
932 if (msg->signal == COutputDataProtocol::PICTURE)
933 {
934 if (m_presentPicture)
935 {
936 m_presentPicture->Release();
937 m_presentPicture = nullptr;
938 }
939
940 m_presentPicture = *(CVaapiRenderPicture**)msg->data;
941 m_bufferStats.DecRender();
942 m_bufferStats.SetParams(0, m_codecControl);
943 msg->Release();
944 return CDVDVideoCodec::VC_PICTURE;
945 }
946 msg->Release();
947 }
948 else if (m_vaapiOutput.m_controlPort.ReceiveInMessage(&msg))
949 {
950 if (msg->signal == COutputControlProtocol::STATS)
951 {
952 msg->Release();
953 m_bufferStats.Get(decoded, processed, render, vpp);
954 if (!drain && decoded < 2 && processed < 3)
955 {
956 return CDVDVideoCodec::VC_BUFFER;
957 }
958 }
959 else
960 {
961 msg->Release();
962 m_DisplayState = VAAPI_ERROR;
963 return CDVDVideoCodec::VC_ERROR;
964 }
965 }
966
967 if (!m_inMsgEvent.WaitMSec(2000))
968 break;
969 }
970
971 CLog::Log(LOGERROR, "VAAPI::%s - timed out waiting for output message - decoded: %d, proc: %d, has free surface: %s",
972 __FUNCTION__, decoded, processed, m_videoSurfaces.HasFree() ? "yes" : "no");
973 m_DisplayState = VAAPI_ERROR;
974
975 return CDVDVideoCodec::VC_ERROR;
976 }
977
Check(AVCodecContext * avctx)978 CDVDVideoCodec::VCReturn CDecoder::Check(AVCodecContext* avctx)
979 {
980 EDisplayState state;
981
982 { CSingleLock lock(m_DecoderSection);
983 state = m_DisplayState;
984 }
985
986 if (state == VAAPI_LOST)
987 {
988 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::Check waiting for display reset event");
989 if (!m_DisplayEvent.WaitMSec(4000))
990 {
991 CLog::Log(LOGERROR, "VAAPI::Check - device didn't reset in reasonable time");
992 state = VAAPI_RESET;
993 }
994 else
995 {
996 CSingleLock lock(m_DecoderSection);
997 state = m_DisplayState;
998 }
999 }
1000 if (state == VAAPI_RESET || state == VAAPI_ERROR)
1001 {
1002 CSingleLock lock(m_DecoderSection);
1003
1004 avcodec_flush_buffers(avctx);
1005 FiniVAAPIOutput();
1006 if (m_vaapiConfig.context)
1007 m_vaapiConfig.context->Release(this);
1008 m_vaapiConfig.context = 0;
1009
1010 if (CVAAPIContext::EnsureContext(&m_vaapiConfig.context, this) && ConfigVAAPI())
1011 {
1012 m_DisplayState = VAAPI_OPEN;
1013 }
1014
1015 if (state == VAAPI_RESET)
1016 return CDVDVideoCodec::VC_FLUSHED;
1017 else
1018 return CDVDVideoCodec::VC_ERROR;
1019 }
1020
1021 if (m_getBufferError > 0 && m_getBufferError < 5)
1022 {
1023 // if there is no other error, sleep for a short while
1024 // in order not to drain player's message queue
1025 KODI::TIME::Sleep(10);
1026
1027 return CDVDVideoCodec::VC_NOBUFFER;
1028 }
1029
1030 return CDVDVideoCodec::VC_NONE;
1031 }
1032
GetPicture(AVCodecContext * avctx,VideoPicture * picture)1033 bool CDecoder::GetPicture(AVCodecContext* avctx, VideoPicture* picture)
1034 {
1035 if (picture->videoBuffer)
1036 {
1037 picture->videoBuffer->Release();
1038 picture->videoBuffer = nullptr;
1039 }
1040
1041 CSingleLock lock(m_DecoderSection);
1042
1043 if (m_DisplayState != VAAPI_OPEN)
1044 return false;
1045
1046 picture->SetParams(m_presentPicture->DVDPic);
1047 picture->videoBuffer = m_presentPicture;
1048 m_presentPicture = nullptr;
1049
1050 return true;
1051 }
1052
Reset()1053 void CDecoder::Reset()
1054 {
1055 CSingleLock lock(m_DecoderSection);
1056
1057 if (m_presentPicture)
1058 {
1059 m_presentPicture->Release();
1060 m_presentPicture = nullptr;
1061 }
1062
1063 if (!m_vaapiConfigured)
1064 return;
1065
1066 Message *reply;
1067 if (m_vaapiOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH,
1068 &reply,
1069 2000))
1070 {
1071 bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1072 reply->Release();
1073 if (!success)
1074 {
1075 CLog::Log(LOGERROR, "VAAPI::%s - flush returned error", __FUNCTION__);
1076 m_DisplayState = VAAPI_ERROR;
1077 }
1078 else
1079 {
1080 m_bufferStats.Reset();
1081 m_getBufferError = 0;
1082 }
1083 }
1084 else
1085 {
1086 CLog::Log(LOGERROR, "VAAPI::%s - flush timed out", __FUNCTION__);
1087 m_DisplayState = VAAPI_ERROR;
1088 }
1089 }
1090
CanSkipDeint()1091 bool CDecoder::CanSkipDeint()
1092 {
1093 return m_bufferStats.CanSkipDeint();
1094 }
1095
CheckSuccess(VAStatus status,const std::string & function)1096 bool CDecoder::CheckSuccess(VAStatus status, const std::string& function)
1097 {
1098 if (status != VA_STATUS_SUCCESS)
1099 {
1100 CLog::Log(LOGERROR, "VAAPI/decoder {} error: {} ({})", function, vaErrorStr(status), status);
1101 m_ErrorCount++;
1102
1103 if(m_DisplayState == VAAPI_OPEN)
1104 {
1105 if (m_ErrorCount > 2)
1106 m_DisplayState = VAAPI_ERROR;
1107 }
1108 return false;
1109 }
1110 m_ErrorCount = 0;
1111 return true;
1112 }
1113
ConfigVAAPI()1114 bool CDecoder::ConfigVAAPI()
1115 {
1116 m_vaapiConfig.dpy = m_vaapiConfig.context->GetDisplay();
1117 m_vaapiConfig.attrib = m_vaapiConfig.context->GetAttrib(m_vaapiConfig.profile);
1118 if ((m_vaapiConfig.attrib.value & (VA_RT_FORMAT_YUV420 | VA_RT_FORMAT_YUV420_10BPP)) == 0)
1119 {
1120 CLog::Log(LOGERROR, "VAAPI - invalid yuv format %x", m_vaapiConfig.attrib.value);
1121 return false;
1122 }
1123
1124 m_vaapiConfig.configId = m_vaapiConfig.context->CreateConfig(m_vaapiConfig.profile,
1125 m_vaapiConfig.attrib);
1126 if (m_vaapiConfig.configId == VA_INVALID_ID)
1127 return false;
1128
1129 // create surfaces
1130 unsigned int format = VA_RT_FORMAT_YUV420;
1131 std::int32_t pixelFormat = VA_FOURCC_NV12;
1132
1133 if (m_vaapiConfig.profile == VAProfileHEVCMain10)
1134 {
1135 format = VA_RT_FORMAT_YUV420_10BPP;
1136 pixelFormat = VA_FOURCC_P010;
1137 }
1138
1139 VASurfaceAttrib attribs[1], *attrib;
1140 attrib = attribs;
1141 attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
1142 attrib->type = VASurfaceAttribPixelFormat;
1143 attrib->value.type = VAGenericValueTypeInteger;
1144 attrib->value.value.i = pixelFormat;
1145
1146 VASurfaceID surfaces[32];
1147 int nb_surfaces = m_vaapiConfig.maxReferences;
1148 if (!CheckSuccess(
1149 vaCreateSurfaces(m_vaapiConfig.dpy, format, m_vaapiConfig.surfaceWidth,
1150 m_vaapiConfig.surfaceHeight, surfaces,
1151 nb_surfaces, attribs, 1), "vaCreateSurfaces"))
1152 {
1153 return false;
1154 }
1155 for (int i=0; i<nb_surfaces; i++)
1156 {
1157 m_videoSurfaces.AddSurface(surfaces[i]);
1158 }
1159
1160 // initialize output
1161 CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
1162 m_vaapiConfig.stats = &m_bufferStats;
1163 m_bufferStats.Reset();
1164 m_vaapiOutput.Start();
1165 Message *reply;
1166 if (m_vaapiOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT,
1167 &reply,
1168 2000,
1169 &m_vaapiConfig,
1170 sizeof(m_vaapiConfig)))
1171 {
1172 bool success = reply->signal == COutputControlProtocol::ACC ? true : false;
1173 if (!success)
1174 {
1175 reply->Release();
1176 CLog::Log(LOGERROR, "VAAPI::%s - vaapi output returned error", __FUNCTION__);
1177 m_vaapiOutput.Dispose();
1178 return false;
1179 }
1180 reply->Release();
1181 }
1182 else
1183 {
1184 CLog::Log(LOGERROR, "VAAPI::%s - failed to init output", __FUNCTION__);
1185 m_vaapiOutput.Dispose();
1186 return false;
1187 }
1188
1189 m_inMsgEvent.Reset();
1190 m_vaapiConfigured = true;
1191 m_ErrorCount = 0;
1192
1193 return true;
1194 }
1195
FiniVAAPIOutput()1196 void CDecoder::FiniVAAPIOutput()
1197 {
1198 if (!m_vaapiConfigured)
1199 return;
1200
1201 // uninit output
1202 m_vaapiOutput.Dispose();
1203 m_vaapiConfigured = false;
1204
1205 // destroy surfaces
1206 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::FiniVAAPIOutput destroying %d video surfaces", m_videoSurfaces.Size());
1207 VASurfaceID surf;
1208 while((surf = m_videoSurfaces.RemoveNext()) != VA_INVALID_SURFACE)
1209 {
1210 CheckSuccess(vaDestroySurfaces(m_vaapiConfig.dpy, &surf, 1), "vaDestroySurfaces");
1211 }
1212 m_videoSurfaces.Reset();
1213
1214 // destroy vaapi config
1215 if (m_vaapiConfig.configId != VA_INVALID_ID)
1216 CheckSuccess(vaDestroyConfig(m_vaapiConfig.dpy, m_vaapiConfig.configId), "vaDestroyConfig");
1217 m_vaapiConfig.configId = VA_INVALID_ID;
1218 }
1219
ReturnRenderPicture(CVaapiRenderPicture * renderPic)1220 void CDecoder::ReturnRenderPicture(CVaapiRenderPicture *renderPic)
1221 {
1222 m_vaapiOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic));
1223 }
1224
Create(CDVDStreamInfo & hint,CProcessInfo & processInfo,AVPixelFormat fmt)1225 IHardwareDecoder* CDecoder::Create(CDVDStreamInfo &hint, CProcessInfo &processInfo, AVPixelFormat fmt)
1226 {
1227 if (fmt == AV_PIX_FMT_VAAPI_VLD && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(SETTING_VIDEOPLAYER_USEVAAPI))
1228 return new VAAPI::CDecoder(processInfo);
1229
1230 return nullptr;
1231 }
1232
Register(IVaapiWinSystem * winSystem,bool deepColor)1233 void CDecoder::Register(IVaapiWinSystem *winSystem, bool deepColor)
1234 {
1235 m_pWinSystem = winSystem;
1236
1237 CVaapiConfig config;
1238 if (!CVAAPIContext::EnsureContext(&config.context, nullptr))
1239 return;
1240
1241 m_capGeneral = true;
1242 m_capDeepColor = deepColor;
1243 CDVDFactoryCodec::RegisterHWAccel("vaapi", CDecoder::Create);
1244 config.context->Release(nullptr);
1245
1246 auto settingsComponent = CServiceBroker::GetSettingsComponent();
1247 if (!settingsComponent)
1248 return;
1249
1250 auto settings = settingsComponent->GetSettings();
1251 if (!settings)
1252 return;
1253
1254 constexpr std::array<const char*, 8> vaapiSettings = {
1255 SETTING_VIDEOPLAYER_USEVAAPI, SETTING_VIDEOPLAYER_USEVAAPIMPEG4,
1256 SETTING_VIDEOPLAYER_USEVAAPIVC1, SETTING_VIDEOPLAYER_USEVAAPIMPEG2,
1257 SETTING_VIDEOPLAYER_USEVAAPIVP8, SETTING_VIDEOPLAYER_USEVAAPIVP9,
1258 SETTING_VIDEOPLAYER_USEVAAPIHEVC, SETTING_VIDEOPLAYER_PREFERVAAPIRENDER};
1259
1260 for (const auto vaapiSetting : vaapiSettings)
1261 {
1262 auto setting = settings->GetSetting(vaapiSetting);
1263 if (!setting)
1264 {
1265 CLog::Log(LOGERROR, "Failed to load setting for: {}", vaapiSetting);
1266 continue;
1267 }
1268
1269 setting->SetVisible(true);
1270 }
1271 }
1272
1273 //-----------------------------------------------------------------------------
1274 // Buffer Pool
1275 //-----------------------------------------------------------------------------
1276
1277 /**
1278 * Buffer pool holds allocated vaapi and gl resources
1279 * Embedded in COutput
1280 */
1281 class VAAPI::CVaapiBufferPool : public IVideoBufferPool
1282 {
1283 public:
1284 explicit CVaapiBufferPool(CDecoder &decoder);
1285 ~CVaapiBufferPool() override;
1286 CVideoBuffer* Get() override;
1287 void Return(int id) override;
1288 CVaapiRenderPicture* GetVaapi();
1289 bool HasFree();
1290 void QueueReturnPicture(CVaapiRenderPicture *pic);
1291 CVaapiRenderPicture* ProcessSyncPicture();
1292 void Init();
1293 void DeleteTextures(bool precleanup);
1294
1295 std::deque<CVaapiProcessedPicture> processedPics;
1296 std::deque<CVaapiProcessedPicture> processedPicsAway;
1297 std::deque<CVaapiDecodedPicture> decodedPics;
1298 int procPicId;
1299
1300 protected:
1301 std::vector<CVaapiRenderPicture*> allRenderPics;
1302 std::deque<int> usedRenderPics;
1303 std::deque<int> freeRenderPics;
1304 std::deque<int> syncRenderPics;
1305
1306 CDecoder &m_vaapi;
1307 };
1308
CVaapiBufferPool(CDecoder & decoder)1309 CVaapiBufferPool::CVaapiBufferPool(CDecoder &decoder)
1310 : m_vaapi(decoder)
1311 {
1312 CVaapiRenderPicture *pic;
1313 for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1314 {
1315 pic = new CVaapiRenderPicture(i);
1316 allRenderPics.push_back(pic);
1317 freeRenderPics.push_back(i);
1318 }
1319 }
1320
~CVaapiBufferPool()1321 CVaapiBufferPool::~CVaapiBufferPool()
1322 {
1323 CVaapiRenderPicture *pic;
1324 for (unsigned int i = 0; i < NUM_RENDER_PICS; i++)
1325 {
1326 pic = allRenderPics[i];
1327 delete pic;
1328 }
1329 allRenderPics.clear();
1330 }
1331
Get()1332 CVideoBuffer* CVaapiBufferPool::Get()
1333 {
1334 if (freeRenderPics.empty())
1335 return nullptr;
1336
1337 int idx = freeRenderPics.front();
1338 freeRenderPics.pop_front();
1339 usedRenderPics.push_back(idx);
1340
1341 CVideoBuffer *retPic = allRenderPics[idx];
1342 retPic->Acquire(GetPtr());
1343
1344 m_vaapi.Acquire();
1345
1346 return retPic;
1347 }
1348
Return(int id)1349 void CVaapiBufferPool::Return(int id)
1350 {
1351 CVaapiRenderPicture *pic = allRenderPics[id];
1352
1353 m_vaapi.ReturnRenderPicture(pic);
1354 m_vaapi.ReleasePicReference();
1355 }
1356
GetVaapi()1357 CVaapiRenderPicture* CVaapiBufferPool::GetVaapi()
1358 {
1359 return dynamic_cast<CVaapiRenderPicture*>(Get());
1360 }
1361
HasFree()1362 bool CVaapiBufferPool::HasFree()
1363 {
1364 return !freeRenderPics.empty();
1365 }
1366
QueueReturnPicture(CVaapiRenderPicture * pic)1367 void CVaapiBufferPool::QueueReturnPicture(CVaapiRenderPicture *pic)
1368 {
1369 std::deque<int>::iterator it;
1370 for (it = usedRenderPics.begin(); it != usedRenderPics.end(); ++it)
1371 {
1372 if (allRenderPics[*it] == pic)
1373 {
1374 break;
1375 }
1376 }
1377
1378 if (it == usedRenderPics.end())
1379 {
1380 CLog::Log(LOGWARNING, "CVaapiRenderPicture::QueueReturnPicture - pic not found");
1381 return;
1382 }
1383
1384 // check if already queued
1385 auto it2 = find(syncRenderPics.begin(), syncRenderPics.end(), *it);
1386 if (it2 == syncRenderPics.end())
1387 {
1388 syncRenderPics.push_back(*it);
1389 }
1390 }
1391
ProcessSyncPicture()1392 CVaapiRenderPicture* CVaapiBufferPool::ProcessSyncPicture()
1393 {
1394 CVaapiRenderPicture *retPic = nullptr;
1395
1396 for (auto it = syncRenderPics.begin(); it != syncRenderPics.end(); ++it)
1397 {
1398 retPic = allRenderPics[*it];
1399
1400 freeRenderPics.push_back(*it);
1401
1402 auto it2 = find(usedRenderPics.begin(), usedRenderPics.end(),*it);
1403 if (it2 == usedRenderPics.end())
1404 {
1405 CLog::Log(LOGERROR, "CVaapiRenderPicture::ProcessSyncPicture - pic not found in queue");
1406 }
1407 else
1408 {
1409 usedRenderPics.erase(it2);
1410 }
1411 it = syncRenderPics.erase(it);
1412
1413 if (!retPic->valid)
1414 {
1415 CLog::Log(LOGDEBUG, LOGVIDEO, "CVaapiRenderPicture::%s - return of invalid render pic", __FUNCTION__);
1416 retPic = nullptr;
1417 }
1418 break;
1419 }
1420 return retPic;
1421 }
1422
Init()1423 void CVaapiBufferPool::Init()
1424 {
1425 for (auto &pic : allRenderPics)
1426 {
1427 pic->avFrame = av_frame_alloc();
1428 pic->valid = false;
1429 }
1430 procPicId = 0;
1431 }
1432
DeleteTextures(bool precleanup)1433 void CVaapiBufferPool::DeleteTextures(bool precleanup)
1434 {
1435 for (auto &pic : allRenderPics)
1436 {
1437 if (precleanup && pic->valid)
1438 continue;
1439
1440 av_frame_free(&pic->avFrame);
1441 pic->valid = false;
1442 }
1443 }
1444
GetPlanes(uint8_t * (& planes)[YuvImage::MAX_PLANES])1445 void CVaapiRenderPicture::GetPlanes(uint8_t*(&planes)[YuvImage::MAX_PLANES])
1446 {
1447 planes[0] = avFrame->data[0];
1448 planes[1] = avFrame->data[1];
1449 planes[2] = avFrame->data[2];
1450 }
1451
GetStrides(int (& strides)[YuvImage::MAX_PLANES])1452 void CVaapiRenderPicture::GetStrides(int(&strides)[YuvImage::MAX_PLANES])
1453 {
1454 strides[0] = avFrame->linesize[0];
1455 strides[1] = avFrame->linesize[1];
1456 strides[2] = avFrame->linesize[2];
1457 }
1458
1459 //-----------------------------------------------------------------------------
1460 // Output
1461 //-----------------------------------------------------------------------------
COutput(CDecoder & decoder,CEvent * inMsgEvent)1462 COutput::COutput(CDecoder &decoder, CEvent *inMsgEvent) :
1463 CThread("Vaapi-Output"),
1464 m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent),
1465 m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent),
1466 m_vaapi(decoder)
1467 {
1468 m_inMsgEvent = inMsgEvent;
1469 m_bufferPool = std::make_shared<CVaapiBufferPool>(decoder);
1470 }
1471
Start()1472 void COutput::Start()
1473 {
1474 Create();
1475 }
1476
~COutput()1477 COutput::~COutput()
1478 {
1479 Dispose();
1480 }
1481
Dispose()1482 void COutput::Dispose()
1483 {
1484 CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
1485 m_bStop = true;
1486 m_outMsgEvent.Set();
1487 StopThread();
1488 m_controlPort.Purge();
1489 m_dataPort.Purge();
1490 }
1491
OnStartup()1492 void COutput::OnStartup()
1493 {
1494 CLog::Log(LOGINFO, "COutput::OnStartup: Output Thread created");
1495 }
1496
OnExit()1497 void COutput::OnExit()
1498 {
1499 CLog::Log(LOGINFO, "COutput::OnExit: Output Thread terminated");
1500 }
1501
1502 enum OUTPUT_STATES
1503 {
1504 O_TOP = 0, // 0
1505 O_TOP_ERROR, // 1
1506 O_TOP_UNCONFIGURED, // 2
1507 O_TOP_CONFIGURED, // 3
1508 O_TOP_CONFIGURED_IDLE, // 4
1509 O_TOP_CONFIGURED_WORK, // 5
1510 O_TOP_CONFIGURED_STEP1, // 6
1511 O_TOP_CONFIGURED_STEP2, // 7
1512 O_TOP_CONFIGURED_OUTPUT, // 8
1513 };
1514
1515 int VAAPI_OUTPUT_parentStates[] = {
1516 -1,
1517 0, //TOP_ERROR
1518 0, //TOP_UNCONFIGURED
1519 0, //TOP_CONFIGURED
1520 3, //TOP_CONFIGURED_IDLE
1521 3, //TOP_CONFIGURED_WORK
1522 3, //TOP_CONFIGURED_STEP1
1523 3, //TOP_CONFIGURED_STEP2
1524 3, //TOP_CONFIGURED_OUTPUT
1525 };
1526
StateMachine(int signal,Protocol * port,Message * msg)1527 void COutput::StateMachine(int signal, Protocol *port, Message *msg)
1528 {
1529 for (int state = m_state; ; state = VAAPI_OUTPUT_parentStates[state])
1530 {
1531 switch (state)
1532 {
1533 case O_TOP: // TOP
1534 if (port == &m_controlPort)
1535 {
1536 switch (signal)
1537 {
1538 case COutputControlProtocol::FLUSH:
1539 msg->Reply(COutputControlProtocol::ACC);
1540 return;
1541 case COutputControlProtocol::PRECLEANUP:
1542 msg->Reply(COutputControlProtocol::ACC);
1543 return;
1544 default:
1545 break;
1546 }
1547 }
1548 else if (port == &m_dataPort)
1549 {
1550 switch (signal)
1551 {
1552 case COutputDataProtocol::RETURNPIC:
1553 CVaapiRenderPicture *pic;
1554 pic = *((CVaapiRenderPicture**)msg->data);
1555 QueueReturnPicture(pic);
1556 return;
1557 case COutputDataProtocol::RETURNPROCPIC:
1558 int id;
1559 id = *((int*)msg->data);
1560 ProcessReturnProcPicture(id);
1561 return;
1562 default:
1563 break;
1564 }
1565 }
1566 {
1567 std::string portName = port == NULL ? "timer" : port->portName;
1568 CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
1569 }
1570 return;
1571
1572 case O_TOP_ERROR:
1573 break;
1574
1575 case O_TOP_UNCONFIGURED:
1576 if (port == &m_controlPort)
1577 {
1578 switch (signal)
1579 {
1580 case COutputControlProtocol::INIT:
1581 CVaapiConfig *data;
1582 data = reinterpret_cast<CVaapiConfig*>(msg->data);
1583 if (data)
1584 {
1585 m_config = *data;
1586 }
1587 Init();
1588
1589 // set initial number of
1590 EnsureBufferPool();
1591 m_state = O_TOP_CONFIGURED_IDLE;
1592 msg->Reply(COutputControlProtocol::ACC);
1593 return;
1594 default:
1595 break;
1596 }
1597 }
1598 break;
1599
1600 case O_TOP_CONFIGURED:
1601 if (port == &m_controlPort)
1602 {
1603 switch (signal)
1604 {
1605 case COutputControlProtocol::FLUSH:
1606 Flush();
1607 msg->Reply(COutputControlProtocol::ACC);
1608 m_state = O_TOP_CONFIGURED_IDLE;
1609 return;
1610 case COutputControlProtocol::PRECLEANUP:
1611 Flush();
1612 ReleaseBufferPool(true);
1613 msg->Reply(COutputControlProtocol::ACC);
1614 m_state = O_TOP_UNCONFIGURED;
1615 m_extTimeout = 10000;
1616 return;
1617 default:
1618 break;
1619 }
1620 }
1621 else if (port == &m_dataPort)
1622 {
1623 switch (signal)
1624 {
1625 case COutputDataProtocol::NEWFRAME:
1626 CPayloadWrap<CVaapiDecodedPicture> *payload;
1627 payload = dynamic_cast<CPayloadWrap<CVaapiDecodedPicture>*>(msg->payloadObj.get());
1628 if (payload)
1629 {
1630 m_bufferPool->decodedPics.push_back(*(payload->GetPlayload()));
1631 m_extTimeout = 0;
1632 }
1633 return;
1634 case COutputDataProtocol::RETURNPIC:
1635 CVaapiRenderPicture *pic;
1636 pic = *((CVaapiRenderPicture**)msg->data);
1637 QueueReturnPicture(pic);
1638 m_controlPort.SendInMessage(COutputControlProtocol::STATS);
1639 m_extTimeout = 0;
1640 return;
1641 case COutputDataProtocol::RETURNPROCPIC:
1642 int id;
1643 id = *((int*)msg->data);
1644 ProcessReturnProcPicture(id);
1645 m_extTimeout = 0;
1646 return;
1647 default:
1648 break;
1649 }
1650 }
1651 break;
1652
1653 case O_TOP_CONFIGURED_IDLE:
1654 if (port == NULL) // timeout
1655 {
1656 switch (signal)
1657 {
1658 case COutputControlProtocol::TIMEOUT:
1659 ProcessSyncPicture();
1660 m_extTimeout = 100;
1661 if (HasWork())
1662 {
1663 m_state = O_TOP_CONFIGURED_WORK;
1664 m_extTimeout = 0;
1665 }
1666 return;
1667 default:
1668 break;
1669 }
1670 }
1671 break;
1672
1673 case O_TOP_CONFIGURED_WORK:
1674 if (port == NULL) // timeout
1675 {
1676 switch (signal)
1677 {
1678 case COutputControlProtocol::TIMEOUT:
1679 if (PreferPP())
1680 {
1681 m_currentPicture = m_bufferPool->decodedPics.front();
1682 m_bufferPool->decodedPics.pop_front();
1683 InitCycle();
1684 m_state = O_TOP_CONFIGURED_STEP1;
1685 m_extTimeout = 0;
1686 return;
1687 }
1688 else if (m_bufferPool->HasFree() &&
1689 !m_bufferPool->processedPics.empty())
1690 {
1691 m_state = O_TOP_CONFIGURED_OUTPUT;
1692 m_extTimeout = 0;
1693 return;
1694 }
1695 else
1696 m_state = O_TOP_CONFIGURED_IDLE;
1697 m_extTimeout = 100;
1698 return;
1699 default:
1700 break;
1701 }
1702 }
1703 break;
1704
1705 case O_TOP_CONFIGURED_STEP1:
1706 if (port == NULL) // timeout
1707 {
1708 switch (signal)
1709 {
1710 case COutputControlProtocol::TIMEOUT:
1711 {
1712 if (!m_pp->AddPicture(m_currentPicture))
1713 {
1714 m_state = O_TOP_ERROR;
1715 return;
1716 }
1717 CVaapiProcessedPicture outPic;
1718 if (m_pp->Filter(outPic))
1719 {
1720 m_config.stats->IncProcessed();
1721 m_bufferPool->processedPics.push_back(outPic);
1722 m_state = O_TOP_CONFIGURED_STEP2;
1723 }
1724 else
1725 {
1726 m_state = O_TOP_CONFIGURED_IDLE;
1727 }
1728 m_config.stats->DecDecoded();
1729 m_controlPort.SendInMessage(COutputControlProtocol::STATS);
1730 m_extTimeout = 0;
1731 return;
1732 }
1733 default:
1734 break;
1735 }
1736 }
1737 break;
1738
1739 case O_TOP_CONFIGURED_STEP2:
1740 if (port == NULL) // timeout
1741 {
1742 switch (signal)
1743 {
1744 case COutputControlProtocol::TIMEOUT:
1745 {
1746 CVaapiProcessedPicture outPic;
1747 if (m_pp->Filter(outPic))
1748 {
1749 m_bufferPool->processedPics.push_back(outPic);
1750 m_config.stats->IncProcessed();
1751 m_extTimeout = 0;
1752 return;
1753 }
1754 m_state = O_TOP_CONFIGURED_IDLE;
1755 m_extTimeout = 0;
1756 return;
1757 }
1758 default:
1759 break;
1760 }
1761 }
1762 break;
1763
1764 case O_TOP_CONFIGURED_OUTPUT:
1765 if (port == NULL) // timeout
1766 {
1767 switch (signal)
1768 {
1769 case COutputControlProtocol::TIMEOUT:
1770 if (!m_bufferPool->processedPics.empty())
1771 {
1772 CVaapiRenderPicture *outPic;
1773 CVaapiProcessedPicture procPic;
1774 procPic = m_bufferPool->processedPics.front();
1775 m_bufferPool->processedPics.pop_front();
1776 outPic = ProcessPicture(procPic);
1777 if (outPic)
1778 {
1779 m_config.stats->IncRender();
1780 m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &outPic, sizeof(outPic));
1781 }
1782 m_config.stats->DecProcessed();
1783 }
1784 m_state = O_TOP_CONFIGURED_IDLE;
1785 m_extTimeout = 0;
1786 return;
1787 default:
1788 break;
1789 }
1790 }
1791 break;
1792
1793 default: // we are in no state, should not happen
1794 CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state);
1795 return;
1796 }
1797 } // for
1798 }
1799
Process()1800 void COutput::Process()
1801 {
1802 Message *msg = NULL;
1803 Protocol *port = NULL;
1804 bool gotMsg;
1805
1806 m_state = O_TOP_UNCONFIGURED;
1807 m_extTimeout = 1000;
1808 m_bStateMachineSelfTrigger = false;
1809
1810 while (!m_bStop)
1811 {
1812 gotMsg = false;
1813
1814 if (m_bStateMachineSelfTrigger)
1815 {
1816 m_bStateMachineSelfTrigger = false;
1817 // self trigger state machine
1818 StateMachine(msg->signal, port, msg);
1819 if (!m_bStateMachineSelfTrigger)
1820 {
1821 msg->Release();
1822 msg = NULL;
1823 }
1824 continue;
1825 }
1826 // check control port
1827 else if (m_controlPort.ReceiveOutMessage(&msg))
1828 {
1829 gotMsg = true;
1830 port = &m_controlPort;
1831 }
1832 // check data port
1833 else if (m_dataPort.ReceiveOutMessage(&msg))
1834 {
1835 gotMsg = true;
1836 port = &m_dataPort;
1837 }
1838 if (gotMsg)
1839 {
1840 StateMachine(msg->signal, port, msg);
1841 if (!m_bStateMachineSelfTrigger)
1842 {
1843 msg->Release();
1844 msg = NULL;
1845 }
1846 continue;
1847 }
1848
1849 // wait for message
1850 else if (m_outMsgEvent.WaitMSec(m_extTimeout))
1851 {
1852 continue;
1853 }
1854 // time out
1855 else
1856 {
1857 msg = m_controlPort.GetMessage();
1858 msg->signal = COutputControlProtocol::TIMEOUT;
1859 port = 0;
1860 // signal timeout to state machine
1861 StateMachine(msg->signal, port, msg);
1862 if (!m_bStateMachineSelfTrigger)
1863 {
1864 msg->Release();
1865 msg = NULL;
1866 }
1867 }
1868 }
1869 Flush();
1870 Uninit();
1871 }
1872
Init()1873 bool COutput::Init()
1874 {
1875 m_diMethods.numDiMethods = 0;
1876
1877 m_pp = new CFFmpegPostproc();
1878 m_pp->PreInit(m_config, &m_diMethods);
1879 delete m_pp;
1880
1881 m_pp = new CVppPostproc();
1882 m_pp->PreInit(m_config, &m_diMethods);
1883 delete m_pp;
1884
1885 m_pp = nullptr;
1886
1887 std::list<EINTERLACEMETHOD> deintMethods;
1888 deintMethods.assign(m_diMethods.diMethods, m_diMethods.diMethods + m_diMethods.numDiMethods);
1889 m_config.processInfo->UpdateDeinterlacingMethods(deintMethods);
1890 m_config.processInfo->SetDeinterlacingMethodDefault(EINTERLACEMETHOD::VS_INTERLACEMETHOD_VAAPI_BOB);
1891
1892 m_seenInterlaced = false;
1893
1894 return true;
1895 }
1896
Uninit()1897 bool COutput::Uninit()
1898 {
1899 ProcessSyncPicture();
1900 ReleaseBufferPool();
1901 if (m_pp)
1902 {
1903 std::shared_ptr<CPostproc> pp(m_pp);
1904 m_discardedPostprocs.push_back(pp);
1905 m_pp->Discard(this, &COutput::ReadyForDisposal);
1906 m_pp = nullptr;
1907 }
1908
1909 if (!m_discardedPostprocs.empty())
1910 {
1911 CLog::Log(LOGERROR, "VAAPI::COutput::Uninit - not all CPostprcs released");
1912 }
1913 return true;
1914 }
1915
Flush()1916 void COutput::Flush()
1917 {
1918 Message *msg;
1919 while (m_dataPort.ReceiveOutMessage(&msg))
1920 {
1921 if (msg->signal == COutputDataProtocol::NEWFRAME)
1922 {
1923 CPayloadWrap<CVaapiDecodedPicture> *payload;
1924 payload = dynamic_cast<CPayloadWrap<CVaapiDecodedPicture>*>(msg->payloadObj.get());
1925 if (payload)
1926 {
1927 CVaapiDecodedPicture pic = *(payload->GetPlayload());
1928 m_config.videoSurfaces->ClearRender(pic.videoSurface);
1929 }
1930 }
1931 else if (msg->signal == COutputDataProtocol::RETURNPIC)
1932 {
1933 CVaapiRenderPicture *pic;
1934 pic = *((CVaapiRenderPicture**)msg->data);
1935 QueueReturnPicture(pic);
1936 }
1937 msg->Release();
1938 }
1939
1940 while (m_dataPort.ReceiveInMessage(&msg))
1941 {
1942 if (msg->signal == COutputDataProtocol::PICTURE)
1943 {
1944 CVaapiRenderPicture *pic;
1945 pic = *((CVaapiRenderPicture**)msg->data);
1946 pic->Release();
1947 }
1948 msg->Release();
1949 }
1950
1951 for (unsigned int i = 0; i < m_bufferPool->decodedPics.size(); i++)
1952 {
1953 m_config.videoSurfaces->ClearRender(m_bufferPool->decodedPics[i].videoSurface);
1954 }
1955 m_bufferPool->decodedPics.clear();
1956
1957 for (unsigned int i = 0; i < m_bufferPool->processedPics.size(); i++)
1958 {
1959 ReleaseProcessedPicture(m_bufferPool->processedPics[i]);
1960 }
1961 m_bufferPool->processedPics.clear();
1962
1963 if (m_pp)
1964 m_pp->Flush();
1965 }
1966
HasWork()1967 bool COutput::HasWork()
1968 {
1969 // send a pic to renderer
1970 if (m_bufferPool->HasFree() && !m_bufferPool->processedPics.empty())
1971 return true;
1972
1973 bool ppWantsPic = true;
1974 if (m_pp)
1975 ppWantsPic = m_pp->WantsPic();
1976
1977 if (!m_bufferPool->decodedPics.empty() && m_bufferPool->processedPics.size() < 4 && ppWantsPic)
1978 return true;
1979
1980 return false;
1981 }
1982
PreferPP()1983 bool COutput::PreferPP()
1984 {
1985 if (!m_bufferPool->decodedPics.empty())
1986 {
1987 if (!m_pp)
1988 return true;
1989
1990 if (!m_pp->WantsPic())
1991 return false;
1992
1993 if (!m_pp->DoesSync() && m_bufferPool->processedPics.size() < 4)
1994 return true;
1995
1996 if (!m_bufferPool->HasFree() || m_bufferPool->processedPics.empty())
1997 return true;
1998 }
1999
2000 return false;
2001 }
2002
InitCycle()2003 void COutput::InitCycle()
2004 {
2005 uint64_t latency;
2006 int flags;
2007 m_config.stats->GetParams(latency, flags);
2008
2009 m_config.stats->SetCanSkipDeint(false);
2010
2011 EINTERLACEMETHOD method = m_config.processInfo->GetVideoSettings().m_InterlaceMethod;
2012 bool interlaced = m_currentPicture.DVDPic.iFlags & DVP_FLAG_INTERLACED;
2013 // Remember whether any interlaced frames were encountered already.
2014 // If this is the case, the deinterlace method will never automatically be switched to NONE again in
2015 // order to not change deint methods every few frames in PAFF streams.
2016 m_seenInterlaced = m_seenInterlaced || interlaced;
2017
2018 if (!(flags & DVD_CODEC_CTRL_NO_POSTPROC) &&
2019 m_seenInterlaced &&
2020 method != VS_INTERLACEMETHOD_NONE)
2021 {
2022 if (!m_config.processInfo->Supports(method))
2023 method = VS_INTERLACEMETHOD_VAAPI_BOB;
2024
2025 if (m_pp && !m_pp->UpdateDeintMethod(method))
2026 {
2027 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Current postproc does not want new deinterlace mode, removing");
2028 std::shared_ptr<CPostproc> pp(m_pp);
2029 m_discardedPostprocs.push_back(pp);
2030 m_pp->Discard(this, &COutput::ReadyForDisposal);
2031 m_pp = nullptr;
2032 m_config.processInfo->SetVideoDeintMethod("unknown");
2033 }
2034 if (!m_pp)
2035 {
2036 if (method == VS_INTERLACEMETHOD_DEINTERLACE ||
2037 method == VS_INTERLACEMETHOD_RENDER_BOB)
2038 {
2039 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing ffmpeg postproc");
2040 m_pp = new CFFmpegPostproc();
2041 m_config.stats->SetVpp(false);
2042 }
2043 else
2044 {
2045 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing vaapi postproc");
2046 m_pp = new CVppPostproc();
2047 m_config.stats->SetVpp(true);
2048 }
2049 if (m_pp->PreInit(m_config))
2050 {
2051 m_pp->Init(method);
2052 }
2053 else
2054 {
2055 CLog::Log(LOGERROR, "VAAPI output: Postproc preinit failed");
2056 delete m_pp;
2057 m_pp = nullptr;
2058 }
2059 }
2060 }
2061 // progressive
2062 else
2063 {
2064 method = VS_INTERLACEMETHOD_NONE;
2065
2066 if (m_pp && !m_pp->UpdateDeintMethod(method))
2067 {
2068 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Current postproc does not want new deinterlace mode, removing");
2069 std::shared_ptr<CPostproc> pp(m_pp);
2070 m_discardedPostprocs.push_back(pp);
2071 m_pp->Discard(this, &COutput::ReadyForDisposal);
2072 m_pp = nullptr;
2073 m_config.processInfo->SetVideoDeintMethod("unknown");
2074 }
2075 if (!m_pp)
2076 {
2077 const bool preferVaapiRender = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(SETTING_VIDEOPLAYER_PREFERVAAPIRENDER);
2078 // For 1080p/i or below, always use CVppPostproc even when not deinterlacing
2079 // Reason is: mesa cannot dynamically switch surfaces between use for VAAPI post-processing
2080 // and use for direct export, so we run into trouble if we or the user want to switch
2081 // deinterlacing on/off mid-stream.
2082 // See also: https://bugs.freedesktop.org/show_bug.cgi?id=105145
2083 const bool alwaysInsertVpp = m_config.driverIsMesa &&
2084 ((m_config.vidWidth * m_config.vidHeight) <= (1920 * 1080)) &&
2085 interlaced;
2086
2087 m_config.stats->SetVpp(false);
2088 if (!preferVaapiRender)
2089 {
2090 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing ffmpeg postproc");
2091 m_pp = new CFFmpegPostproc();
2092 }
2093 else if (alwaysInsertVpp)
2094 {
2095 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing vaapi postproc");
2096 m_pp = new CVppPostproc();
2097 m_config.stats->SetVpp(true);
2098 }
2099 else
2100 {
2101 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing skip postproc");
2102 m_pp = new CSkipPostproc();
2103 }
2104 if (m_pp->PreInit(m_config))
2105 {
2106 m_pp->Init(method);
2107 }
2108 else
2109 {
2110 CLog::Log(LOGERROR, "VAAPI output: Postproc preinit failed");
2111 delete m_pp;
2112 m_pp = nullptr;
2113 }
2114 }
2115 }
2116 if (!m_pp) // fallback
2117 {
2118 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI output: Initializing skip postproc as fallback");
2119 m_pp = new CSkipPostproc();
2120 m_config.stats->SetVpp(false);
2121 if (m_pp->PreInit(m_config))
2122 m_pp->Init(method);
2123 }
2124 }
2125
ProcessPicture(CVaapiProcessedPicture & pic)2126 CVaapiRenderPicture* COutput::ProcessPicture(CVaapiProcessedPicture &pic)
2127 {
2128 CVaapiRenderPicture *retPic;
2129 retPic = m_bufferPool->GetVaapi();
2130 retPic->DVDPic.SetParams(pic.DVDPic);
2131
2132 if (!pic.source)
2133 {
2134 CLog::Log(LOGERROR, "VAAPI::ProcessPicture - pic has no source");
2135 retPic->Release();
2136 return nullptr;
2137 }
2138
2139 if (pic.source->UseVideoSurface())
2140 {
2141 vaSyncSurface(m_config.dpy, pic.videoSurface);
2142 pic.id = m_bufferPool->procPicId++;
2143 m_bufferPool->processedPicsAway.push_back(pic);
2144 retPic->procPic = pic;
2145 retPic->vadsp = m_config.dpy;
2146 }
2147 else
2148 {
2149 av_frame_move_ref(retPic->avFrame, pic.frame);
2150 pic.source->ClearRef(pic);
2151 retPic->procPic.videoSurface = VA_INVALID_ID;
2152 }
2153
2154 retPic->DVDPic.dts = DVD_NOPTS_VALUE;
2155 retPic->DVDPic.iWidth = m_config.vidWidth;
2156 retPic->DVDPic.iHeight = m_config.vidHeight;
2157
2158 retPic->valid = true;
2159
2160 return retPic;
2161 }
2162
ReleaseProcessedPicture(CVaapiProcessedPicture & pic)2163 void COutput::ReleaseProcessedPicture(CVaapiProcessedPicture &pic)
2164 {
2165 if (!pic.source)
2166 {
2167 return;
2168 }
2169 pic.source->ClearRef(pic);
2170 pic.source = nullptr;
2171 }
2172
QueueReturnPicture(CVaapiRenderPicture * pic)2173 void COutput::QueueReturnPicture(CVaapiRenderPicture *pic)
2174 {
2175 m_bufferPool->QueueReturnPicture(pic);
2176 ProcessSyncPicture();
2177 }
2178
ProcessSyncPicture()2179 void COutput::ProcessSyncPicture()
2180 {
2181 CVaapiRenderPicture *pic;
2182
2183 pic = m_bufferPool->ProcessSyncPicture();
2184
2185 if (pic)
2186 {
2187 ProcessReturnPicture(pic);
2188 }
2189 }
2190
ProcessReturnPicture(CVaapiRenderPicture * pic)2191 void COutput::ProcessReturnPicture(CVaapiRenderPicture *pic)
2192 {
2193 if (pic->avFrame)
2194 av_frame_unref(pic->avFrame);
2195
2196 ProcessReturnProcPicture(pic->procPic.id);
2197 pic->valid = false;
2198 }
2199
ProcessReturnProcPicture(int id)2200 void COutput::ProcessReturnProcPicture(int id)
2201 {
2202 for (auto it=m_bufferPool->processedPicsAway.begin(); it!=m_bufferPool->processedPicsAway.end(); ++it)
2203 {
2204 if (it->id == id)
2205 {
2206 ReleaseProcessedPicture(*it);
2207 m_bufferPool->processedPicsAway.erase(it);
2208 break;
2209 }
2210 }
2211 }
2212
EnsureBufferPool()2213 void COutput::EnsureBufferPool()
2214 {
2215 m_bufferPool->Init();
2216 }
2217
ReleaseBufferPool(bool precleanup)2218 void COutput::ReleaseBufferPool(bool precleanup)
2219 {
2220 ProcessSyncPicture();
2221
2222 m_bufferPool->DeleteTextures(precleanup);
2223
2224 for (unsigned int i = 0; i < m_bufferPool->decodedPics.size(); i++)
2225 {
2226 m_config.videoSurfaces->ClearRender(m_bufferPool->decodedPics[i].videoSurface);
2227 }
2228 m_bufferPool->decodedPics.clear();
2229
2230 for (unsigned int i = 0; i < m_bufferPool->processedPics.size(); i++)
2231 {
2232 ReleaseProcessedPicture(m_bufferPool->processedPics[i]);
2233 }
2234 m_bufferPool->processedPics.clear();
2235 }
2236
ReadyForDisposal(CPostproc * pp)2237 void COutput::ReadyForDisposal(CPostproc *pp)
2238 {
2239 for (auto it = m_discardedPostprocs.begin(); it != m_discardedPostprocs.end(); ++it)
2240 {
2241 if ((*it).get() == pp)
2242 {
2243 m_discardedPostprocs.erase(it);
2244 break;
2245 }
2246 }
2247 }
2248
2249 //-----------------------------------------------------------------------------
2250 // Postprocessing
2251 //-----------------------------------------------------------------------------
2252
PreInit(CVaapiConfig & config,SDiMethods * methods)2253 bool CSkipPostproc::PreInit(CVaapiConfig &config, SDiMethods *methods)
2254 {
2255 m_config = config;
2256 return true;
2257 }
2258
Init(EINTERLACEMETHOD method)2259 bool CSkipPostproc::Init(EINTERLACEMETHOD method)
2260 {
2261 m_config.processInfo->SetVideoDeintMethod("none");
2262 return true;
2263 }
2264
AddPicture(CVaapiDecodedPicture & inPic)2265 bool CSkipPostproc::AddPicture(CVaapiDecodedPicture &inPic)
2266 {
2267 m_pic = inPic;
2268 m_step = 0;
2269 return true;
2270 }
2271
Filter(CVaapiProcessedPicture & outPic)2272 bool CSkipPostproc::Filter(CVaapiProcessedPicture &outPic)
2273 {
2274 if (m_step > 0)
2275 return false;
2276 outPic.DVDPic.SetParams(m_pic.DVDPic);
2277 outPic.videoSurface = m_pic.videoSurface;
2278 m_refsToSurfaces++;
2279 outPic.source = this;
2280 outPic.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2281 DVP_FLAG_REPEAT_TOP_FIELD |
2282 DVP_FLAG_INTERLACED);
2283 m_step++;
2284 return true;
2285 }
2286
ClearRef(CVaapiProcessedPicture & pic)2287 void CSkipPostproc::ClearRef(CVaapiProcessedPicture &pic)
2288 {
2289 m_config.videoSurfaces->ClearRender(pic.videoSurface);
2290 m_refsToSurfaces--;
2291
2292 if (m_pOut && m_refsToSurfaces <= 0)
2293 (m_pOut->*m_cbDispose)(this);
2294 }
2295
Flush()2296 void CSkipPostproc::Flush()
2297 {
2298
2299 }
2300
UpdateDeintMethod(EINTERLACEMETHOD method)2301 bool CSkipPostproc::UpdateDeintMethod(EINTERLACEMETHOD method)
2302 {
2303 if (method == VS_INTERLACEMETHOD_NONE)
2304 return true;
2305
2306 return false;
2307 }
2308
DoesSync()2309 bool CSkipPostproc::DoesSync()
2310 {
2311 return false;
2312 }
2313
UseVideoSurface()2314 bool CSkipPostproc::UseVideoSurface()
2315 {
2316 return true;
2317 }
2318
Discard(COutput * output,ReadyToDispose cb)2319 void CSkipPostproc::Discard(COutput *output, ReadyToDispose cb)
2320 {
2321 m_pOut = output;
2322 m_cbDispose = cb;
2323 if (m_refsToSurfaces <= 0)
2324 (m_pOut->*m_cbDispose)(this);
2325 }
2326
2327 //-----------------------------------------------------------------------------
2328 // VPP Postprocessing
2329 //-----------------------------------------------------------------------------
2330
CVppPostproc()2331 CVppPostproc::CVppPostproc()
2332 {
2333 m_contextId = VA_INVALID_ID;
2334 m_configId = VA_INVALID_ID;
2335 m_filter = VA_INVALID_ID;
2336 }
2337
~CVppPostproc()2338 CVppPostproc::~CVppPostproc()
2339 {
2340 Dispose();
2341 }
2342
PreInit(CVaapiConfig & config,SDiMethods * methods)2343 bool CVppPostproc::PreInit(CVaapiConfig &config, SDiMethods *methods)
2344 {
2345 m_config = config;
2346
2347 // create config
2348 if (!CheckSuccess(
2349 vaCreateConfig(m_config.dpy, VAProfileNone, VAEntrypointVideoProc, NULL, 0, &m_configId),
2350 "vaCreateConfig"))
2351 {
2352 CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit - VPP init failed in vaCreateConfig");
2353
2354 return false;
2355 }
2356
2357 VASurfaceAttrib attribs[1], *attrib;
2358 attrib = attribs;
2359 attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
2360 attrib->type = VASurfaceAttribPixelFormat;
2361 attrib->value.type = VAGenericValueTypeInteger;
2362 attrib->value.value.i = VA_FOURCC_NV12;
2363
2364 // create surfaces
2365 VASurfaceID surfaces[32];
2366 unsigned int format = VA_RT_FORMAT_YUV420;
2367 if (m_config.profile == VAProfileHEVCMain10)
2368 {
2369 format = VA_RT_FORMAT_YUV420_10BPP;
2370 attrib->value.value.i = VA_FOURCC_P010;
2371 }
2372 int nb_surfaces = NUM_RENDER_PICS;
2373 if (!CheckSuccess(
2374 vaCreateSurfaces(m_config.dpy, format, m_config.surfaceWidth, m_config.surfaceHeight,
2375 surfaces, nb_surfaces,
2376 attribs, 1), "vaCreateSurfaces"))
2377 {
2378 CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit - VPP init failed in vaCreateSurfaces");
2379
2380 return false;
2381 }
2382 for (int i=0; i<nb_surfaces; i++)
2383 {
2384 m_videoSurfaces.AddSurface(surfaces[i]);
2385 }
2386
2387 // create vaapi decoder context
2388 if (!CheckSuccess(
2389 vaCreateContext(m_config.dpy, m_configId, m_config.surfaceWidth, m_config.surfaceHeight, 0,
2390 surfaces,
2391 nb_surfaces, &m_contextId), "vaCreateContext"))
2392 {
2393 m_contextId = VA_INVALID_ID;
2394 CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit - VPP init failed in vaCreateContext");
2395
2396 return false;
2397 }
2398
2399 VAProcFilterType filters[VAProcFilterCount];
2400 unsigned int numFilters = VAProcFilterCount;
2401 VAProcFilterCapDeinterlacing deinterlacingCaps[VAProcDeinterlacingCount];
2402 unsigned int numDeinterlacingCaps = VAProcDeinterlacingCount;
2403
2404 if (!CheckSuccess(vaQueryVideoProcFilters(m_config.dpy, m_contextId, filters, &numFilters),
2405 "vaQueryVideoProcFilters"))
2406 {
2407 CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit - VPP init failed in vaQueryVideoProcFilters");
2408
2409 return false;
2410 }
2411
2412 if (!CheckSuccess(vaQueryVideoProcFilterCaps(m_config.dpy, m_contextId, VAProcFilterDeinterlacing,
2413 deinterlacingCaps,
2414 &numDeinterlacingCaps), "vaQueryVideoProcFilterCaps"))
2415 {
2416 CLog::Log(LOGDEBUG, LOGVIDEO, "CVppPostproc::PreInit - VPP init failed in vaQueryVideoProcFilterCaps");
2417
2418 return false;
2419 }
2420
2421 if (methods)
2422 {
2423 for (unsigned int i = 0; i < numFilters; i++)
2424 {
2425 if (filters[i] == VAProcFilterDeinterlacing)
2426 {
2427 for (unsigned int j = 0; j < numDeinterlacingCaps; j++)
2428 {
2429 if (deinterlacingCaps[j].type == VAProcDeinterlacingBob)
2430 {
2431 methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_VAAPI_BOB;
2432 }
2433 else if (deinterlacingCaps[j].type == VAProcDeinterlacingMotionAdaptive)
2434 {
2435 methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_VAAPI_MADI;
2436 }
2437 else if (deinterlacingCaps[j].type == VAProcDeinterlacingMotionCompensated)
2438 {
2439 methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_VAAPI_MACI;
2440 }
2441 }
2442 }
2443 }
2444 }
2445 return true;
2446 }
2447
Init(EINTERLACEMETHOD method)2448 bool CVppPostproc::Init(EINTERLACEMETHOD method)
2449 {
2450 m_forwardRefs = 0;
2451 m_backwardRefs = 0;
2452 m_currentIdx = 0;
2453 m_frameCount = 0;
2454 m_vppMethod = VS_INTERLACEMETHOD_AUTO;
2455
2456 return UpdateDeintMethod(method);
2457 }
2458
2459
UpdateDeintMethod(EINTERLACEMETHOD method)2460 bool CVppPostproc::UpdateDeintMethod(EINTERLACEMETHOD method)
2461 {
2462 if (method == m_vppMethod)
2463 {
2464 return true;
2465 }
2466
2467 m_vppMethod = method;
2468 m_forwardRefs = 0;
2469 m_backwardRefs = 0;
2470
2471 if (m_filter != VA_INVALID_ID)
2472 {
2473 CheckSuccess(vaDestroyBuffer(m_config.dpy, m_filter), "vaDestroyBuffer");
2474 m_filter = VA_INVALID_ID;
2475 }
2476
2477 VAProcDeinterlacingType vppMethod;
2478 switch (method)
2479 {
2480 case VS_INTERLACEMETHOD_VAAPI_BOB:
2481 vppMethod = VAProcDeinterlacingBob;
2482 m_config.processInfo->SetVideoDeintMethod("vaapi-bob");
2483 break;
2484 case VS_INTERLACEMETHOD_VAAPI_MADI:
2485 vppMethod = VAProcDeinterlacingMotionAdaptive;
2486 m_config.processInfo->SetVideoDeintMethod("vaapi-madi");
2487 break;
2488 case VS_INTERLACEMETHOD_VAAPI_MACI:
2489 vppMethod = VAProcDeinterlacingMotionCompensated;
2490 m_config.processInfo->SetVideoDeintMethod("vaapi-mcdi");
2491 break;
2492 case VS_INTERLACEMETHOD_NONE:
2493 // Early exit, filter parameter buffer not needed then
2494 m_config.processInfo->SetVideoDeintMethod("vaapi-none");
2495 return true;
2496 default:
2497 m_config.processInfo->SetVideoDeintMethod("unknown");
2498 return false;
2499 }
2500
2501 VAProcFilterParameterBufferDeinterlacing filterparams;
2502 filterparams.type = VAProcFilterDeinterlacing;
2503 filterparams.algorithm = vppMethod;
2504 filterparams.flags = 0;
2505
2506 if (!CheckSuccess(vaCreateBuffer(m_config.dpy, m_contextId, VAProcFilterParameterBufferType,
2507 sizeof(filterparams), 1,
2508 &filterparams, &m_filter), "vaCreateBuffer"))
2509 {
2510 m_filter = VA_INVALID_ID;
2511 return false;
2512 }
2513
2514 VAProcPipelineCaps pplCaps;
2515 if (!CheckSuccess(vaQueryVideoProcPipelineCaps(m_config.dpy, m_contextId, &m_filter, 1, &pplCaps),
2516 "vaQueryVideoProcPipelineCaps"))
2517 {
2518 return false;
2519 }
2520
2521 m_forwardRefs = pplCaps.num_forward_references;
2522 m_backwardRefs = pplCaps.num_backward_references;
2523
2524 return true;
2525 }
2526
Dispose()2527 void CVppPostproc::Dispose()
2528 {
2529 // make sure surfaces are idle
2530 for (int i=0; i<m_videoSurfaces.Size(); i++)
2531 {
2532 CheckSuccess(vaSyncSurface(m_config.dpy, m_videoSurfaces.GetAtIndex(i)), "vaSyncSurface");
2533 }
2534
2535 if (m_filter != VA_INVALID_ID)
2536 {
2537 CheckSuccess(vaDestroyBuffer(m_config.dpy, m_filter), "vaDestroyBuffer");
2538 m_filter = VA_INVALID_ID;
2539 }
2540 if (m_contextId != VA_INVALID_ID)
2541 {
2542 CheckSuccess(vaDestroyContext(m_config.dpy, m_contextId), "vaDestroyContext");
2543 m_contextId = VA_INVALID_ID;
2544 }
2545 VASurfaceID surf;
2546 while((surf = m_videoSurfaces.RemoveNext()) != VA_INVALID_SURFACE)
2547 {
2548 CheckSuccess(vaDestroySurfaces(m_config.dpy, &surf, 1), "vaDestroySurface");
2549 }
2550 m_videoSurfaces.Reset();
2551
2552 if (m_configId != VA_INVALID_ID)
2553 {
2554 CheckSuccess(vaDestroyConfig(m_config.dpy, m_configId), "vaDestroyConfig");
2555 m_configId = VA_INVALID_ID;
2556 }
2557
2558 // release all decoded pictures
2559 Flush();
2560 }
2561
AddPicture(CVaapiDecodedPicture & pic)2562 bool CVppPostproc::AddPicture(CVaapiDecodedPicture &pic)
2563 {
2564 pic.index = m_frameCount;
2565 m_decodedPics.push_front(pic);
2566 m_frameCount++;
2567 m_step = 0;
2568 m_config.stats->SetCanSkipDeint(true);
2569 return true;
2570 }
2571
Filter(CVaapiProcessedPicture & outPic)2572 bool CVppPostproc::Filter(CVaapiProcessedPicture &outPic)
2573 {
2574 if (m_step>1)
2575 {
2576 Advance();
2577 return false;
2578 }
2579
2580 // we need a free render target
2581 VASurfaceID surf = m_videoSurfaces.GetFree(VA_INVALID_SURFACE);
2582 if (surf == VA_INVALID_SURFACE)
2583 {
2584 CLog::Log(LOGERROR, "VAAPI - VPP - no free render target");
2585 return false;
2586 }
2587 // clear reference in case we return false
2588 m_videoSurfaces.ClearReference(surf);
2589
2590 // move window of frames we are looking at to account for backward (=future) refs
2591 const auto currentIdx = m_currentIdx - m_backwardRefs;
2592
2593 // make sure we have all needed forward refs
2594 if ((currentIdx - m_forwardRefs) < m_decodedPics.back().index)
2595 {
2596 Advance();
2597 return false;
2598 }
2599
2600 auto it = std::find_if(m_decodedPics.begin(), m_decodedPics.end(),
2601 [currentIdx](const CVaapiDecodedPicture &picture){
2602 return picture.index == currentIdx;
2603 });
2604 if (it==m_decodedPics.end())
2605 {
2606 return false;
2607 }
2608 outPic.DVDPic.SetParams(it->DVDPic);
2609
2610 // skip deinterlacing cycle if requested
2611 if ((m_step == 1) &&
2612 ((outPic.DVDPic.iFlags & DVD_CODEC_CTRL_SKIPDEINT) || !(outPic.DVDPic.iFlags & DVP_FLAG_INTERLACED) || (m_vppMethod == VS_INTERLACEMETHOD_NONE)))
2613 {
2614 Advance();
2615 return false;
2616 }
2617
2618 // vpp deinterlacing
2619 VAProcFilterParameterBufferDeinterlacing *filterParams;
2620 VABufferID pipelineBuf;
2621 VAProcPipelineParameterBuffer *pipelineParams;
2622 VARectangle inputRegion;
2623 VARectangle outputRegion;
2624
2625 if (!CheckSuccess(vaBeginPicture(m_config.dpy, m_contextId, surf), "vaBeginPicture"))
2626 {
2627 return false;
2628 }
2629
2630 if (!CheckSuccess(vaCreateBuffer(m_config.dpy, m_contextId, VAProcPipelineParameterBufferType,
2631 sizeof(VAProcPipelineParameterBuffer), 1, NULL, &pipelineBuf), "vaCreateBuffer"))
2632 {
2633 return false;
2634 }
2635 if (!CheckSuccess(vaMapBuffer(m_config.dpy, pipelineBuf, (void**) &pipelineParams),
2636 "vaMapBuffer"))
2637 {
2638 return false;
2639 }
2640 memset(pipelineParams, 0, sizeof(VAProcPipelineParameterBuffer));
2641
2642 inputRegion.x = outputRegion.x = 0;
2643 inputRegion.y = outputRegion.y = 0;
2644 inputRegion.width = outputRegion.width = m_config.surfaceWidth;
2645 inputRegion.height = outputRegion.height = m_config.surfaceHeight;
2646
2647 pipelineParams->output_region = &outputRegion;
2648 pipelineParams->surface_region = &inputRegion;
2649 pipelineParams->output_background_color = 0xff000000;
2650 pipelineParams->filter_flags = 0;
2651
2652 VASurfaceID forwardRefs[32];
2653 VASurfaceID backwardRefs[32];
2654 pipelineParams->forward_references = forwardRefs;
2655 pipelineParams->backward_references = backwardRefs;
2656 pipelineParams->num_forward_references = 0;
2657 pipelineParams->num_backward_references = 0;
2658
2659 int maxPic = currentIdx + m_backwardRefs;
2660 int minPic = currentIdx - m_forwardRefs;
2661 int curPic = currentIdx;
2662
2663 // deinterlace flag
2664 if (m_vppMethod != VS_INTERLACEMETHOD_NONE)
2665 {
2666 unsigned int flags = 0;
2667
2668 if (it->DVDPic.iFlags & DVP_FLAG_INTERLACED)
2669 {
2670 if (it->DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
2671 flags = 0;
2672 else
2673 flags = VA_DEINTERLACING_BOTTOM_FIELD_FIRST | VA_DEINTERLACING_BOTTOM_FIELD;
2674
2675 if (m_step)
2676 {
2677 if (flags & VA_DEINTERLACING_BOTTOM_FIELD)
2678 flags &= ~VA_DEINTERLACING_BOTTOM_FIELD;
2679 else
2680 flags |= VA_DEINTERLACING_BOTTOM_FIELD;
2681 }
2682 }
2683 if (!CheckSuccess(vaMapBuffer(m_config.dpy, m_filter, (void**) &filterParams), "vaMapBuffer"))
2684 {
2685 return false;
2686 }
2687 filterParams->flags = flags;
2688 if (!CheckSuccess(vaUnmapBuffer(m_config.dpy, m_filter), "vaUnmapBuffer"))
2689 {
2690 return false;
2691 }
2692
2693 pipelineParams->filters = &m_filter;
2694 pipelineParams->num_filters = 1;
2695 }
2696 else
2697 {
2698 pipelineParams->num_filters = 0;
2699 }
2700
2701 // references
2702 double ptsLast = DVD_NOPTS_VALUE;
2703 double pts = DVD_NOPTS_VALUE;
2704
2705 pipelineParams->surface = VA_INVALID_SURFACE;
2706 for (const auto &picture : m_decodedPics)
2707 {
2708 if (picture.index >= minPic && picture.index <= maxPic)
2709 {
2710 if (picture.index > curPic)
2711 {
2712 backwardRefs[(picture.index - curPic) - 1] = picture.videoSurface;
2713 pipelineParams->num_backward_references++;
2714 }
2715 else if (picture.index == curPic)
2716 {
2717 pipelineParams->surface = picture.videoSurface;
2718 pts = picture.DVDPic.pts;
2719 }
2720 if (picture.index < curPic)
2721 {
2722 forwardRefs[(curPic - picture.index) - 1] = picture.videoSurface;
2723 pipelineParams->num_forward_references++;
2724 if (picture.index == curPic - 1)
2725 ptsLast = picture.DVDPic.pts;
2726 }
2727 }
2728 }
2729
2730 // set pts for 2nd frame
2731 if (m_step && pts != DVD_NOPTS_VALUE && ptsLast != DVD_NOPTS_VALUE)
2732 outPic.DVDPic.pts += (pts-ptsLast)/2;
2733
2734 if (pipelineParams->surface == VA_INVALID_SURFACE)
2735 return false;
2736
2737 if (!CheckSuccess(vaUnmapBuffer(m_config.dpy, pipelineBuf), "vaUnmmapBuffer"))
2738 {
2739 return false;
2740 }
2741
2742 if (!CheckSuccess(vaRenderPicture(m_config.dpy, m_contextId, &pipelineBuf, 1), "vaRenderPicture"))
2743 {
2744 return false;
2745 }
2746
2747 if (!CheckSuccess(vaEndPicture(m_config.dpy, m_contextId), "vaEndPicture"))
2748 {
2749 return false;
2750 }
2751
2752 if (!CheckSuccess(vaDestroyBuffer(m_config.dpy, pipelineBuf), "vaDestroyBuffer"))
2753 {
2754 return false;
2755 }
2756
2757 m_step++;
2758 outPic.videoSurface = m_videoSurfaces.GetFree(surf);
2759 outPic.source = this;
2760 outPic.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
2761 DVP_FLAG_REPEAT_TOP_FIELD |
2762 DVP_FLAG_INTERLACED);
2763
2764 return true;
2765 }
2766
Advance()2767 void CVppPostproc::Advance()
2768 {
2769 m_currentIdx++;
2770
2771 // release all unneeded refs
2772 auto it = m_decodedPics.begin();
2773 while (it != m_decodedPics.end())
2774 {
2775 if (it->index < m_currentIdx - m_forwardRefs - m_backwardRefs)
2776 {
2777 m_config.videoSurfaces->ClearRender(it->videoSurface);
2778 it = m_decodedPics.erase(it);
2779 }
2780 else
2781 ++it;
2782 }
2783 }
2784
ClearRef(CVaapiProcessedPicture & pic)2785 void CVppPostproc::ClearRef(CVaapiProcessedPicture &pic)
2786 {
2787 m_videoSurfaces.ClearReference(pic.videoSurface);
2788
2789 if (m_pOut && !m_videoSurfaces.HasRefs())
2790 (m_pOut->*m_cbDispose)(this);
2791 }
2792
Flush()2793 void CVppPostproc::Flush()
2794 {
2795 // release all decoded pictures
2796 auto it = m_decodedPics.begin();
2797 while (it != m_decodedPics.end())
2798 {
2799 m_config.videoSurfaces->ClearRender(it->videoSurface);
2800 it = m_decodedPics.erase(it);
2801 }
2802 }
2803
DoesSync()2804 bool CVppPostproc::DoesSync()
2805 {
2806 return false;
2807 }
2808
WantsPic()2809 bool CVppPostproc::WantsPic()
2810 {
2811 // need at least 2 for deinterlacing
2812 if (m_videoSurfaces.NumFree() > 1)
2813 return true;
2814
2815 return false;
2816 }
2817
UseVideoSurface()2818 bool CVppPostproc::UseVideoSurface()
2819 {
2820 return true;
2821 }
2822
Discard(COutput * output,ReadyToDispose cb)2823 void CVppPostproc::Discard(COutput *output, ReadyToDispose cb)
2824 {
2825 m_pOut = output;
2826 m_cbDispose = cb;
2827 if (!m_videoSurfaces.HasRefs())
2828 (m_pOut->*m_cbDispose)(this);
2829 }
2830
CheckSuccess(VAStatus status,const std::string & function)2831 bool CVppPostproc::CheckSuccess(VAStatus status, const std::string& function)
2832 {
2833 if (status != VA_STATUS_SUCCESS)
2834 {
2835 CLog::Log(LOGERROR, "VAAPI/vpp {} error: {} ({})", function, vaErrorStr(status), status);
2836 return false;
2837 }
2838 return true;
2839 }
2840
2841 //-----------------------------------------------------------------------------
2842 // FFmpeg Postprocessing
2843 //-----------------------------------------------------------------------------
2844
2845 #define CACHED_BUFFER_SIZE 4096
2846
CFFmpegPostproc()2847 CFFmpegPostproc::CFFmpegPostproc()
2848 {
2849 m_cache = NULL;
2850 m_pFilterFrameIn = NULL;
2851 m_pFilterFrameOut = NULL;
2852 m_pFilterGraph = NULL;
2853 m_DVDPic.pts = DVD_NOPTS_VALUE;
2854 m_frametime = 0;
2855 m_lastOutPts = DVD_NOPTS_VALUE;
2856 }
2857
~CFFmpegPostproc()2858 CFFmpegPostproc::~CFFmpegPostproc()
2859 {
2860 Close();
2861 KODI::MEMORY::AlignedFree(m_cache);
2862 m_dllSSE4.Unload();
2863 av_frame_free(&m_pFilterFrameIn);
2864 av_frame_free(&m_pFilterFrameOut);
2865 }
2866
PreInit(CVaapiConfig & config,SDiMethods * methods)2867 bool CFFmpegPostproc::PreInit(CVaapiConfig &config, SDiMethods *methods)
2868 {
2869 m_config = config;
2870 bool use_filter = true;
2871
2872 // copying large surfaces via sse4 is a bit slow
2873 // we just return false here as the primary use case the
2874 // sse4 copy method is deinterlacing of max 1080i content
2875 if (m_config.vidWidth > 1920 || m_config.vidHeight > 1088)
2876 return false;
2877
2878 VAImage image;
2879 image.image_id = VA_INVALID_ID;
2880 VASurfaceID surface = config.videoSurfaces->GetAtIndex(0);
2881 VAStatus status = vaDeriveImage(config.dpy, surface, &image);
2882 if (status != VA_STATUS_SUCCESS)
2883 {
2884 CLog::Log(LOGINFO, "VAAPI::SupportsFilter vaDeriveImage not supported by driver - ffmpeg postprocessing and CPU-copy rendering will not be available");
2885 use_filter = false;
2886 }
2887 if (use_filter && (image.format.fourcc != VA_FOURCC_NV12))
2888 {
2889 CLog::Log(LOGWARNING,"VAAPI::SupportsFilter image format not NV12");
2890 use_filter = false;
2891 }
2892 if (use_filter && ((image.pitches[0] % 64) || (image.pitches[1] % 64)))
2893 {
2894 CLog::Log(LOGWARNING,"VAAPI::SupportsFilter patches no multiple of 64");
2895 use_filter = false;
2896 }
2897 if (image.image_id != VA_INVALID_ID)
2898 CheckSuccess(vaDestroyImage(config.dpy, image.image_id), "vaDestroyImage");
2899
2900 if (use_filter && !m_dllSSE4.Load())
2901 {
2902 CLog::Log(LOGERROR,"VAAPI::SupportsFilter failed loading sse4 lib");
2903 use_filter = false;
2904 }
2905
2906 if (use_filter)
2907 {
2908 m_cache = static_cast<uint8_t*>(KODI::MEMORY::AlignedMalloc(CACHED_BUFFER_SIZE, 64));
2909 if (methods)
2910 {
2911 methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_DEINTERLACE;
2912 methods->diMethods[methods->numDiMethods++] = VS_INTERLACEMETHOD_RENDER_BOB;
2913 }
2914 }
2915 return use_filter;
2916 }
2917
Init(EINTERLACEMETHOD method)2918 bool CFFmpegPostproc::Init(EINTERLACEMETHOD method)
2919 {
2920 if (!(m_pFilterGraph = avfilter_graph_alloc()))
2921 {
2922 CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init - unable to alloc filter graph");
2923 return false;
2924 }
2925
2926 const AVFilter* srcFilter = avfilter_get_by_name("buffer");
2927 const AVFilter* outFilter = avfilter_get_by_name("buffersink");
2928
2929 std::string args = StringUtils::Format("%d:%d:%d:%d:%d:%d:%d",
2930 m_config.vidWidth,
2931 m_config.vidHeight,
2932 AV_PIX_FMT_NV12,
2933 1,
2934 1,
2935 (m_config.aspect.num != 0) ? m_config.aspect.num : 1,
2936 (m_config.aspect.num != 0) ? m_config.aspect.den : 1);
2937
2938 if (avfilter_graph_create_filter(&m_pFilterIn, srcFilter, "src", args.c_str(), NULL, m_pFilterGraph) < 0)
2939 {
2940 CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init - avfilter_graph_create_filter: src");
2941 return false;
2942 }
2943
2944 if (avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", NULL, NULL, m_pFilterGraph) < 0)
2945 {
2946 CLog::Log(LOGERROR, "CFFmpegPostproc::Init - avfilter_graph_create_filter: out");
2947 return false;
2948 }
2949
2950 enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_NV12, AV_PIX_FMT_NONE };
2951 if (av_opt_set_int_list(m_pFilterOut, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN) < 0)
2952 {
2953 CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init - failed settings pix formats");
2954 return false;
2955 }
2956
2957 AVFilterInOut* outputs = avfilter_inout_alloc();
2958 AVFilterInOut* inputs = avfilter_inout_alloc();
2959
2960 outputs->name = av_strdup("in");
2961 outputs->filter_ctx = m_pFilterIn;
2962 outputs->pad_idx = 0;
2963 outputs->next = NULL;
2964
2965 inputs->name = av_strdup("out");
2966 inputs->filter_ctx = m_pFilterOut;
2967 inputs->pad_idx = 0;
2968 inputs->next = NULL;
2969
2970 if (method == VS_INTERLACEMETHOD_DEINTERLACE)
2971 {
2972 std::string filter;
2973
2974 filter = "yadif=1:-1";
2975
2976 if (avfilter_graph_parse_ptr(m_pFilterGraph, filter.c_str(), &inputs, &outputs, NULL) < 0)
2977 {
2978 CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init - avfilter_graph_parse");
2979 avfilter_inout_free(&outputs);
2980 avfilter_inout_free(&inputs);
2981 return false;
2982 }
2983
2984 avfilter_inout_free(&outputs);
2985 avfilter_inout_free(&inputs);
2986
2987 if (avfilter_graph_config(m_pFilterGraph, NULL) < 0)
2988 {
2989 CLog::Log(LOGERROR, "VAAPI::CFFmpegPostproc::Init - avfilter_graph_config");
2990 return false;
2991 }
2992
2993 m_config.processInfo->SetVideoDeintMethod("yadif");
2994 }
2995 else if (method == VS_INTERLACEMETHOD_RENDER_BOB ||
2996 method == VS_INTERLACEMETHOD_NONE)
2997 {
2998 CLog::Log(LOGDEBUG, LOGVIDEO, "VAAPI::CFFmpegPostproc::Init - skip deinterlacing");
2999 avfilter_inout_free(&outputs);
3000 avfilter_inout_free(&inputs);
3001 m_config.processInfo->SetVideoDeintMethod("none");
3002 }
3003 else
3004 {
3005 avfilter_inout_free(&outputs);
3006 avfilter_inout_free(&inputs);
3007 m_config.processInfo->SetVideoDeintMethod("unknown");
3008 return false;
3009 }
3010 m_diMethod = method;
3011
3012 m_pFilterFrameIn = av_frame_alloc();
3013 m_pFilterFrameOut = av_frame_alloc();
3014 return true;
3015 }
3016
AddPicture(CVaapiDecodedPicture & inPic)3017 bool CFFmpegPostproc::AddPicture(CVaapiDecodedPicture &inPic)
3018 {
3019 VASurfaceID surf = inPic.videoSurface;
3020 VAImage image;
3021 uint8_t *buf;
3022 if (m_DVDPic.pts != DVD_NOPTS_VALUE && inPic.DVDPic.pts != DVD_NOPTS_VALUE)
3023 {
3024 m_frametime = inPic.DVDPic.pts - m_DVDPic.pts;
3025 }
3026 m_DVDPic.SetParams(inPic.DVDPic);
3027 bool result = false;
3028
3029 if (!CheckSuccess(vaSyncSurface(m_config.dpy, surf), "vaSyncSurface"))
3030 goto error;
3031
3032 if (!CheckSuccess(vaDeriveImage(m_config.dpy, surf, &image), "vaDeriveImage"))
3033 goto error;
3034
3035 if (!CheckSuccess(vaMapBuffer(m_config.dpy, image.buf, (void**) &buf), "vaMapBuffer"))
3036 goto error;
3037
3038 m_pFilterFrameIn->format = AV_PIX_FMT_NV12;
3039 m_pFilterFrameIn->width = m_config.vidWidth;
3040 m_pFilterFrameIn->height = m_config.vidHeight;
3041 m_pFilterFrameIn->linesize[0] = image.pitches[0];
3042 m_pFilterFrameIn->linesize[1] = image.pitches[1];
3043 m_pFilterFrameIn->interlaced_frame = (inPic.DVDPic.iFlags & DVP_FLAG_INTERLACED) ? 1 : 0;
3044 m_pFilterFrameIn->top_field_first = (inPic.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST) ? 1 : 0;
3045
3046 if (inPic.DVDPic.pts == DVD_NOPTS_VALUE)
3047 m_pFilterFrameIn->pts = AV_NOPTS_VALUE;
3048 else
3049 m_pFilterFrameIn->pts = (inPic.DVDPic.pts / DVD_TIME_BASE) * AV_TIME_BASE;
3050
3051 m_pFilterFrameIn->pkt_dts = m_pFilterFrameIn->pts;
3052 m_pFilterFrameIn->best_effort_timestamp = m_pFilterFrameIn->pts;
3053
3054 av_frame_get_buffer(m_pFilterFrameIn, 64);
3055
3056 uint8_t *src, *dst;
3057 src = buf + image.offsets[0];
3058 dst = m_pFilterFrameIn->data[0];
3059 m_dllSSE4.copy_frame(src, dst, m_cache, m_config.vidWidth, m_config.vidHeight, image.pitches[0]);
3060
3061 src = buf + image.offsets[1];
3062 dst = m_pFilterFrameIn->data[1];
3063 m_dllSSE4.copy_frame(src, dst, m_cache, image.width, image.height/2, image.pitches[1]);
3064
3065 m_pFilterFrameIn->linesize[0] = image.pitches[0];
3066 m_pFilterFrameIn->linesize[1] = image.pitches[1];
3067 m_pFilterFrameIn->data[2] = NULL;
3068 m_pFilterFrameIn->data[3] = NULL;
3069 m_pFilterFrameIn->pkt_size = image.data_size;
3070
3071 CheckSuccess(vaUnmapBuffer(m_config.dpy, image.buf), "vaUnmapBuffer");
3072 CheckSuccess(vaDestroyImage(m_config.dpy, image.image_id), "vaDestroyImage");
3073
3074 if (m_diMethod == VS_INTERLACEMETHOD_DEINTERLACE)
3075 {
3076 if (av_buffersrc_add_frame(m_pFilterIn, m_pFilterFrameIn) < 0)
3077 {
3078 CLog::Log(LOGERROR, "CFFmpegPostproc::AddPicture - av_buffersrc_add_frame");
3079 goto error;
3080 }
3081 }
3082 else if (m_diMethod == VS_INTERLACEMETHOD_RENDER_BOB ||
3083 m_diMethod == VS_INTERLACEMETHOD_NONE)
3084 {
3085 av_frame_move_ref(m_pFilterFrameOut, m_pFilterFrameIn);
3086 m_step = 0;
3087 }
3088 av_frame_unref(m_pFilterFrameIn);
3089
3090 result = true;
3091
3092 error:
3093 m_config.videoSurfaces->ClearRender(surf);
3094 return result;
3095 }
3096
Filter(CVaapiProcessedPicture & outPic)3097 bool CFFmpegPostproc::Filter(CVaapiProcessedPicture &outPic)
3098 {
3099 outPic.DVDPic.SetParams(m_DVDPic);
3100 if (m_diMethod == VS_INTERLACEMETHOD_DEINTERLACE)
3101 {
3102 int result;
3103 result = av_buffersink_get_frame(m_pFilterOut, m_pFilterFrameOut);
3104
3105 if(result == AVERROR(EAGAIN) || result == AVERROR_EOF)
3106 return false;
3107 else if(result < 0)
3108 {
3109 CLog::Log(LOGERROR, "CFFmpegPostproc::Filter - av_buffersink_get_frame");
3110 return false;
3111 }
3112 outPic.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST |
3113 DVP_FLAG_REPEAT_TOP_FIELD |
3114 DVP_FLAG_INTERLACED);
3115 }
3116 else if (m_diMethod == VS_INTERLACEMETHOD_RENDER_BOB ||
3117 m_diMethod == VS_INTERLACEMETHOD_NONE)
3118 {
3119 if (m_step > 0)
3120 return false;
3121 }
3122
3123 m_step++;
3124 outPic.frame = av_frame_clone(m_pFilterFrameOut);
3125 av_frame_unref(m_pFilterFrameOut);
3126
3127 outPic.source = this;
3128 m_refsToPics++;
3129
3130 int64_t bpts = outPic.frame->best_effort_timestamp;
3131 if(bpts != AV_NOPTS_VALUE)
3132 {
3133 outPic.DVDPic.pts = (double)bpts * DVD_TIME_BASE / AV_TIME_BASE;
3134 }
3135 else
3136 outPic.DVDPic.pts = DVD_NOPTS_VALUE;
3137
3138 double pts = outPic.DVDPic.pts;
3139 if (m_lastOutPts != DVD_NOPTS_VALUE && m_lastOutPts == pts)
3140 {
3141 outPic.DVDPic.pts += m_frametime/2;
3142 }
3143 m_lastOutPts = pts;
3144
3145 return true;
3146 }
3147
ClearRef(CVaapiProcessedPicture & pic)3148 void CFFmpegPostproc::ClearRef(CVaapiProcessedPicture &pic)
3149 {
3150 av_frame_free(&pic.frame);
3151 m_refsToPics--;
3152
3153 if (m_pOut && m_refsToPics <= 0 && m_cbDispose)
3154 (m_pOut->*m_cbDispose)(this);
3155 }
3156
Close()3157 void CFFmpegPostproc::Close()
3158 {
3159 if (m_pFilterGraph)
3160 {
3161 avfilter_graph_free(&m_pFilterGraph);
3162 }
3163 }
3164
Flush()3165 void CFFmpegPostproc::Flush()
3166 {
3167 Close();
3168 Init(m_diMethod);
3169 m_DVDPic.pts = DVD_NOPTS_VALUE;
3170 m_frametime = 0;
3171 m_lastOutPts = DVD_NOPTS_VALUE;
3172 }
3173
UpdateDeintMethod(EINTERLACEMETHOD method)3174 bool CFFmpegPostproc::UpdateDeintMethod(EINTERLACEMETHOD method)
3175 {
3176 /// \todo switching between certain methods could be done without deinit/init
3177 return (m_diMethod == method);
3178 }
3179
DoesSync()3180 bool CFFmpegPostproc::DoesSync()
3181 {
3182 return true;
3183 }
3184
UseVideoSurface()3185 bool CFFmpegPostproc::UseVideoSurface()
3186 {
3187 return false;
3188 }
3189
Discard(COutput * output,ReadyToDispose cb)3190 void CFFmpegPostproc::Discard(COutput *output, ReadyToDispose cb)
3191 {
3192 m_pOut = output;
3193 m_cbDispose = cb;
3194 if (m_refsToPics <= 0)
3195 (m_pOut->*m_cbDispose)(this);
3196 }
3197
CheckSuccess(VAStatus status,const std::string & function)3198 bool CFFmpegPostproc::CheckSuccess(VAStatus status, const std::string& function)
3199 {
3200 if (status != VA_STATUS_SUCCESS)
3201 {
3202 CLog::Log(LOGERROR, "VAAPI/ffpp error: {} ({})", function, vaErrorStr(status), status);
3203 return false;
3204 }
3205 return true;
3206 }
3207