2#include "libmythbase/mythconfig.h"
13#include "libavfilter/buffersrc.h"
14#include "libavfilter/buffersink.h"
15#include "libavutil/cpu.h"
19#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
20#include <QtProcessorDetection>
23#ifdef Q_PROCESSOR_X86_64
24# include <emmintrin.h>
25static const bool s_haveSIMD =
true;
26#elif HAVE_INTRINSICS_NEON
28static const bool s_haveSIMD = av_get_cpu_flags() & AV_CPU_FLAG_NEON;
31#define LOC QString("MythDeint: ")
82 if (
Frame->m_alreadyDeinterlaced)
93 bool doublerate =
true;
94 bool topfieldfirst =
Frame->m_interlacedReverse ? !
Frame->m_topFieldFirst :
Frame->m_topFieldFirst;
109 if (!deinterlacer || other)
131 bool fieldorderchanged = topfieldfirst !=
m_topFirst;
132 if (fieldorderchanged && deinterlacer !=
DEINT_HIGH)
134 fieldorderchanged =
false;
142 if ((deinterlacer ==
DEINT_HIGH) && fieldorderchanged)
149 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Enabled 'auto' for field order");
154 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Disabled 'auto' for field order");
157 fieldorderchanged =
false;
162 if (fieldorderchanged || otherchanged)
164 LOG(VB_GENERAL, LOG_INFO,
LOC +
165 QString(
"Deinterlacer change: %1x%2 %3 dr:%4 tff:%5 -> %6x%7 %8 dr:%9 tff:%10")
170 .arg(doublerate).arg(topfieldfirst));
171 if (!
Initialise(
Frame, deinterlacer, doublerate, topfieldfirst, Profile))
180 if (!
Initialise(
Frame, deinterlacer, doublerate, topfieldfirst, Profile))
215 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error converting frame");
224 auto AddFrame = [](AVFilterContext*
Source,
AVFrame* AvFrame)
225 {
return av_buffersrc_add_frame(
Source, AvFrame); };
232 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error adding frame");
245 if (res == AVERROR(EAGAIN))
247 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error retrieving frame");
255 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Filter returned unexpected format");
261 for (
uint plane = 0; plane < count; ++plane)
269 Frame->m_timecode = std::chrono::milliseconds(
m_frame->pts);
270 Frame->m_alreadyDeinterlaced =
true;
279 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Removing CPU deinterlacer");
290 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Removing 'bob' cache frame");
327 nullptr,
nullptr,
nullptr);
331 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Using deinterlacer '%1'").arg(name));
338 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Inconsistent frame formats");
342 m_graph = avfilter_graph_alloc();
349 threads =
std::clamp(Profile->
GetMaxCPUs(), 1U, std::max(8U, std::thread::hardware_concurrency()));
352 AVFilterInOut* inputs =
nullptr;
353 AVFilterInOut* outputs =
nullptr;
358 else if (TopFieldFirst)
360 auto deint = QString(
"yadif=mode=%1:parity=%2:threads=%3")
361 .arg(DoubleRate ? 1 : 0).arg(parity).arg(threads);
363 auto graph = QString(
"buffer=video_size=%1x%2:pix_fmt=%3:time_base=1/1[in];[in]%4[out];[out] buffersink")
366 int res = avfilter_graph_parse2(
m_graph, graph.toLatin1().constData(), &inputs, &outputs);
367 if (res >= 0 && !inputs && !outputs)
369 res = avfilter_graph_config(
m_graph,
nullptr);
373 m_sink = avfilter_graph_get_filter(
m_graph,
"Parsed_buffersink_2");
377 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Created deinterlacer '%1' (%2 threads)")
378 .arg(name).arg(threads));
389 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create avfilter");
391 avfilter_inout_free(&inputs);
392 avfilter_inout_free(&outputs);
412 LOG(VB_PLAYBACK, LOG_INFO,
"Created new 'bob' cache frame");
437 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error converting frame");
442 dstframe.width =
Frame->m_width;
443 dstframe.height =
Frame->m_height;
444 dstframe.format =
Frame->m_pixFmt;
451 m_frame->flags &= ~AV_FRAME_FLAG_INTERLACED;
453 for (
uint i = 0; i < nbplanes; i++)
462 dstframe.data, dstframe.linesize);
464 if (result !=
Frame->m_height)
466 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Error scaling frame: height %1 expected %2")
467 .arg(result).arg(
Frame->m_height));
469 Frame->m_alreadyDeinterlaced =
true;
472inline static uint32_t
avg(uint32_t A, uint32_t B)
474 return (((A ^ B) & 0xFEFEFEFEUL) >> 1) + (A & B);
478static inline void BlendC4x4(
unsigned char *Src,
int Width,
int FirstRow,
int LastRow,
int Pitch,
479 unsigned char *Dst,
int DstPitch,
bool Second)
481 int srcpitch = Pitch << 1;
482 int dstpitch = DstPitch << 1;
483 int maxrows = LastRow - 3;
485 unsigned char *above = Src + ((FirstRow - 1) *
static_cast<ptrdiff_t
>(Pitch));
486 unsigned char *dest1 = Dst + (FirstRow *
static_cast<ptrdiff_t
>(DstPitch));
487 unsigned char *middle = above + srcpitch;
488 unsigned char *dest2 = dest1 + dstpitch;
489 unsigned char *below = middle + srcpitch;
490 unsigned char *dstcpy1 = Dst + ((FirstRow - 1) *
static_cast<ptrdiff_t
>(DstPitch));
491 unsigned char *dstcpy2 = dstcpy1 + dstpitch;
497 for (
int row = FirstRow; row < maxrows; row += 4)
502 memcpy(dstcpy1, above,
static_cast<size_t>(DstPitch));
503 memcpy(dstcpy2, middle,
static_cast<size_t>(DstPitch));
507 for (
int col = 0; col < Width; col += 4)
509 *
reinterpret_cast<uint32_t*
>(&dest1[col]) =
510 avg(*
reinterpret_cast<uint32_t*
>(&above[col]), *
reinterpret_cast<uint32_t*
>(&middle[col]));
511 *
reinterpret_cast<uint32_t*
>(&dest2[col]) =
512 avg(*
reinterpret_cast<uint32_t*
>(&middle[col]), *
reinterpret_cast<uint32_t*
>(&below[col]));
522#if defined(Q_PROCESSOR_X86_64) || HAVE_INTRINSICS_NEON
524static inline void BlendSIMD16x4(
unsigned char *Src,
int Width,
int FirstRow,
int LastRow,
int Pitch,
525 unsigned char *Dst,
int DstPitch,
bool Second)
527 int srcpitch = Pitch << 1;
528 int dstpitch = DstPitch << 1;
529 int maxrows = LastRow - 3;
531 unsigned char *above = Src + ((FirstRow - 1) *
static_cast<ptrdiff_t
>(Pitch));
532 unsigned char *dest1 = Dst + (FirstRow *
static_cast<ptrdiff_t
>(DstPitch));
533 unsigned char *middle = above + srcpitch;
534 unsigned char *dest2 = dest1 + dstpitch;
535 unsigned char *below = middle + srcpitch;
536 unsigned char *dstcpy1 = Dst + ((FirstRow - 1) *
static_cast<ptrdiff_t
>(DstPitch));
537 unsigned char *dstcpy2 = dstcpy1 + dstpitch;
543 for (
int row = FirstRow; row < maxrows; row += 4)
548 memcpy(dstcpy1, above,
static_cast<size_t>(DstPitch));
549 memcpy(dstcpy2, middle,
static_cast<size_t>(DstPitch));
553 for (
int col = 0; col < Width; col += 16)
555#ifdef Q_PROCESSOR_X86_64
556 __m128i mid = *
reinterpret_cast<__m128i*
>(&middle[col]);
557 *
reinterpret_cast<__m128i*
>(&dest1[col]) =
558 _mm_avg_epu8(*
reinterpret_cast<__m128i*
>(&above[col]), mid);
559 *
reinterpret_cast<__m128i*
>(&dest2[col]) =
560 _mm_avg_epu8(*
reinterpret_cast<__m128i*
>(&below[col]), mid);
562#if HAVE_INTRINSICS_NEON
563 uint8x16_t mid = *
reinterpret_cast<uint8x16_t*
>(&middle[col]);
564 *
reinterpret_cast<uint8x16_t*
>(&dest1[col]) =
565 vrhaddq_u8(*
reinterpret_cast<uint8x16_t*
>(&above[col]), mid);
566 *
reinterpret_cast<uint8x16_t*
>(&dest2[col]) =
567 vrhaddq_u8(*
reinterpret_cast<uint8x16_t*
>(&below[col]), mid);
579static inline void BlendSIMD8x4(
unsigned char *Src,
int Width,
int FirstRow,
int LastRow,
int Pitch,
580 unsigned char *Dst,
int DstPitch,
bool Second)
582 int srcpitch = Pitch << 1;
583 int dstpitch = DstPitch << 1;
584 int maxrows = LastRow - 3;
586 unsigned char *above = Src + ((FirstRow - 1) *
static_cast<ptrdiff_t
>(Pitch));
587 unsigned char *dest1 = Dst + (FirstRow *
static_cast<ptrdiff_t
>(DstPitch));
588 unsigned char *middle = above + srcpitch;
589 unsigned char *dest2 = dest1 + dstpitch;
590 unsigned char *below = middle + srcpitch;
591 unsigned char *dstcpy1 = Dst + ((FirstRow - 1) *
static_cast<ptrdiff_t
>(DstPitch));
592 unsigned char *dstcpy2 = dstcpy1 + dstpitch;
598 for (
int row = FirstRow; row < maxrows; row += 4)
603 memcpy(dstcpy1, above,
static_cast<size_t>(DstPitch));
604 memcpy(dstcpy2, middle,
static_cast<size_t>(DstPitch));
608 for (
int col = 0; col < Width; col += 16)
610#ifdef Q_PROCESSOR_X86_64
611 __m128i mid = *
reinterpret_cast<__m128i*
>(&middle[col]);
612 *
reinterpret_cast<__m128i*
>(&dest1[col]) =
613 _mm_avg_epu16(*
reinterpret_cast<__m128i*
>(&above[col]), mid);
614 *
reinterpret_cast<__m128i*
>(&dest2[col]) =
615 _mm_avg_epu16(*
reinterpret_cast<__m128i*
>(&below[col]), mid);
617#if HAVE_INTRINSICS_NEON
618 uint16x8_t mid = *
reinterpret_cast<uint16x8_t*
>(&middle[col]);
619 *
reinterpret_cast<uint16x8_t*
>(&dest1[col]) =
620 vrhaddq_u16(*
reinterpret_cast<uint16x8_t*
>(&above[col]), mid);
621 *
reinterpret_cast<uint16x8_t*
>(&dest2[col]) =
622 vrhaddq_u16(*
reinterpret_cast<uint16x8_t*
>(&below[col]), mid);
636 if (
Frame->m_height < 16 ||
Frame->m_width < 16)
657 for (
uint plane = 0; plane < count; plane++)
660 int firstrow = top ? 1 : 2;
661 bool height4 = (height % 4) == 0;
662 bool width4 = (src->
m_pitches[plane] % 4) == 0;
665#if defined(Q_PROCESSOR_X86_64) || HAVE_INTRINSICS_NEON
666 bool width16 = (src->
m_pitches[plane] % 16) == 0;
668 if (s_haveSIMD && height4 && width16)
692 if (width4 && height4 && !hidepth)
701 Frame->m_alreadyDeinterlaced =
true;
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
static int FillAVFrame(AVFrame *Frame, const MythVideoFrame *From, AVPixelFormat Fmt=AV_PIX_FMT_NONE)
Initialise AVFrame with content from MythVideoFrame.
static AVPixelFormat FrameTypeToPixelFormat(VideoFrameType Type)
void OneField(MythVideoFrame *Frame, FrameScanType Scan)
bool Initialise(MythVideoFrame *Frame, MythDeintType Deinterlacer, bool DoubleRate, bool TopFieldFirst, MythVideoProfile *Profile)
Initialise deinterlacing using the given MythDeintType.
void Filter(MythVideoFrame *Frame, FrameScanType Scan, MythVideoProfile *Profile, bool Force=false)
Deinterlace Frame if needed.
VideoFrameType m_inputType
MythDeintType m_deintType
uint64_t m_lastFieldChange
void Blend(MythVideoFrame *Frame, FrameScanType Scan)
uint64_t m_discontinuityCounter
AVFilterContext * m_source
MythVideoFrame * m_bobFrame
bool SetUpCache(MythVideoFrame *Frame)
SwsContext * m_swsContext
static uint GetNumPlanes(VideoFrameType Type)
static int GetPitchForPlane(VideoFrameType Type, int Width, uint Plane)
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
static bool FormatIsNV12(VideoFrameType Type)
static QString FormatDescription(VideoFrameType Type)
static int GetHeightForPlane(VideoFrameType Type, int Height, uint Plane)
static uint8_t * GetAlignedBuffer(size_t Size)
static bool YUVFormat(VideoFrameType Type)
static void CopyPlane(uint8_t *To, int ToPitch, const uint8_t *From, int FromPitch, int PlaneWidth, int PlaneHeight)
static int ColorDepth(int Format)
static int GetWidthForPlane(VideoFrameType Type, int Width, uint Plane)
static void BlendC4x4(unsigned char *Src, int Width, int FirstRow, int LastRow, int Pitch, unsigned char *Dst, int DstPitch, bool Second)
static uint32_t avg(uint32_t A, uint32_t B)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static QString Source(const QNetworkRequest &request)
static eu8 clamp(eu8 value, eu8 low, eu8 high)
bool is_interlaced(FrameScanType Scan)