MythTV master
mythdeinterlacer.cpp
Go to the documentation of this file.
1// MythTV
2#include "libmythbase/mythconfig.h"
4
5#include "mythavutil.h"
6#include "mythdeinterlacer.h"
7#include "mythvideoprofile.h"
8
9#include <algorithm>
10#include <thread>
11
12extern "C" {
13#include "libavfilter/buffersrc.h"
14#include "libavfilter/buffersink.h"
15#include "libavutil/cpu.h"
16}
17
18#include <QtGlobal>
19
20#ifdef Q_PROCESSOR_X86_64
21# include <emmintrin.h>
22static const bool s_haveSIMD = true;
23#elif HAVE_INTRINSICS_NEON
24# include <arm_neon.h>
25static const bool s_haveSIMD = av_get_cpu_flags() & AV_CPU_FLAG_NEON;
26#endif
27
28#define LOC QString("MythDeint: ")
29
48{
49 Cleanup();
50}
51
70 MythVideoProfile *Profile, bool Force)
71{
72 // nothing to see here
73 if (!Frame || !is_interlaced(Scan))
74 {
75 Cleanup();
76 return;
77 }
78
79 if (Frame->m_alreadyDeinterlaced)
80 return;
81
82 // Sanity check frame format
83 if (!MythVideoFrame::YUVFormat(Frame->m_type))
84 {
85 Cleanup();
86 return;
87 }
88
89 // check for software deinterlacing
90 bool doublerate = true;
91 bool topfieldfirst = Frame->m_interlacedReverse ? !Frame->m_topFieldFirst : Frame->m_topFieldFirst;
92
93 auto deinterlacer = Frame->GetDoubleRateOption(DEINT_CPU);
94 auto other = Frame->GetDoubleRateOption(DEINT_SHADER);
95 if (other)
96 {
97 Cleanup();
98 return;
99 }
100
101 if (!deinterlacer)
102 {
103 doublerate = false;
104 deinterlacer = Frame->GetSingleRateOption(DEINT_CPU);
105 other = Frame->GetSingleRateOption(DEINT_SHADER);
106 if (!deinterlacer || other)
107 {
108 Cleanup();
109 return;
110 }
111 }
112
113 // libavfilter will not deinterlace NV12 frames. Allow shaders in this case.
114 // libswscale (for bob/onefield) is fine, as is our linearblend.
115 if ((deinterlacer == DEINT_HIGH) && MythVideoFrame::FormatIsNV12(Frame->m_type))
116 {
117 Cleanup();
118 Frame->m_deinterlaceSingle = Frame->m_deinterlaceSingle | DEINT_SHADER;
119 Frame->m_deinterlaceDouble = Frame->m_deinterlaceDouble | DEINT_SHADER;
120 return;
121 }
122
123 // certain material (telecined?) continually changes the field order. This
124 // cripples performance as the libavfiler deinterlacer is continually
125 // destroyed and recreated. Using 'auto' for the field order breaks user
126 // override of the interlacing order - so track switches in the field order
127 // and switch to auto if it is too frequent
128 bool fieldorderchanged = topfieldfirst != m_topFirst;
129 if (fieldorderchanged && deinterlacer != DEINT_HIGH)
130 {
131 fieldorderchanged = false;
132 m_topFirst = topfieldfirst;
133 }
134
135 bool otherchanged = Frame->m_width != m_width || Frame->m_height != m_height ||
136 deinterlacer != m_deintType || doublerate != m_doubleRate ||
137 Frame->m_type != m_inputType;
138
139 if ((deinterlacer == DEINT_HIGH) && fieldorderchanged)
140 {
141 bool alreadyauto = m_autoFieldOrder;
142 bool change = m_lastFieldChange && (qAbs(m_lastFieldChange - Frame->m_frameCounter) < 10);
143 if (change && !m_autoFieldOrder)
144 {
145 m_autoFieldOrder = true;
146 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Enabled 'auto' for field order");
147 }
148 else if (!change && m_autoFieldOrder)
149 {
150 m_autoFieldOrder = false;
151 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Disabled 'auto' for field order");
152 }
153 if (alreadyauto && m_autoFieldOrder)
154 fieldorderchanged = false;
155 m_lastFieldChange = Frame->m_frameCounter;
156 }
157
158 // Check for a change in input or deinterlacer
159 if (fieldorderchanged || otherchanged)
160 {
161 LOG(VB_GENERAL, LOG_INFO, LOC +
162 QString("Deinterlacer change: %1x%2 %3 dr:%4 tff:%5 -> %6x%7 %8 dr:%9 tff:%10")
164 .arg(m_doubleRate).arg(m_topFirst)
165 .arg(Frame->m_width).arg(Frame->m_height)
167 .arg(doublerate).arg(topfieldfirst));
168 if (!Initialise(Frame, deinterlacer, doublerate, topfieldfirst, Profile))
169 {
170 Cleanup();
171 return;
172 }
173 Force = true;
174 }
175 else if ((m_deintType == DEINT_HIGH) && (qAbs(Frame->m_frameCounter - m_discontinuityCounter) > 1))
176 {
177 if (!Initialise(Frame, deinterlacer, doublerate, topfieldfirst, Profile))
178 {
179 Cleanup();
180 return;
181 }
182 Force = true;
183 }
184
185 m_discontinuityCounter = Frame->m_frameCounter;
186
187 // Set in use deinterlacer for debugging
188 Frame->m_deinterlaceInuse = m_deintType | DEINT_CPU;
189 Frame->m_deinterlaceInuse2x = m_doubleRate;
190
191 // onefield or bob
193 {
194 OneField(Frame, Scan);
195 return;
196 }
197
198 // linear blend
200 {
201 Blend(Frame, Scan);
202 return;
203 }
204
205 // We need a filter
206 if (!m_graph)
207 return;
208
209 // Convert VideoFrame to AVFrame - no copy
211 {
212 LOG(VB_GENERAL, LOG_ERR, LOC + "Error converting frame");
213 return;
214 }
215
216 m_frame->width = Frame->m_width;
217 m_frame->height = Frame->m_height;
218 m_frame->format = Frame->m_pixFmt;
219 m_frame->pts = Frame->m_timecode.count();
220
221 auto AddFrame = [](AVFilterContext* Source, AVFrame* AvFrame)
222 { return av_buffersrc_add_frame(Source, AvFrame); };
223
224 // Add frame on first pass only
225 if (kScan_Interlaced == Scan)
226 {
227 if (AddFrame(m_source, m_frame) < 0)
228 {
229 LOG(VB_GENERAL, LOG_ERR, LOC + "Error adding frame");
230 return;
231 }
232 // yadif needs 2 frames to work with - add the frame again if we
233 // need a result now
234 if (Force)
235 AddFrame(m_source, m_frame);
236 }
237
238 // Retrieve frame
239 int res = av_buffersink_get_frame(m_sink, m_frame);
240 if (res < 0)
241 {
242 if (res == AVERROR(EAGAIN))
243 return;
244 LOG(VB_GENERAL, LOG_ERR, LOC + "Error retrieving frame");
245 return;
246 }
247
248 // Ensure AVFrame is in the expected format
249 if ((m_frame->format != m_inputFmt) || (Frame->m_pitches[0] < m_frame->linesize[0]) ||
250 (Frame->m_pitches[1] < m_frame->linesize[1]) || (Frame->m_pitches[2] < m_frame->linesize[2]))
251 {
252 LOG(VB_GENERAL, LOG_ERR, LOC + "Filter returned unexpected format");
253 return;
254 }
255
256 // Copy AVFrame back to VideoFrame
257 uint count = MythVideoFrame::GetNumPlanes(Frame->m_type);
258 for (uint plane = 0; plane < count; ++plane)
259 {
260 MythVideoFrame::CopyPlane(Frame->m_buffer + Frame->m_offsets[plane], Frame->m_pitches[plane],
261 m_frame->data[plane], m_frame->linesize[plane],
264 }
265
266 Frame->m_timecode = std::chrono::milliseconds(m_frame->pts);
267 Frame->m_alreadyDeinterlaced = true;
268
269 // Free frame data
270 av_frame_unref(m_frame);
271}
272
274{
275 if (m_graph || m_swsContext)
276 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Removing CPU deinterlacer");
277
278 avfilter_graph_free(&m_graph);
279 sws_freeContext(m_swsContext);
280 m_swsContext = nullptr;
282 m_autoFieldOrder = false;
284
285 if (m_bobFrame)
286 {
287 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Removing 'bob' cache frame");
288 delete m_bobFrame;
289 m_bobFrame = nullptr;
290 }
291
293}
294
297 bool DoubleRate, bool TopFieldFirst, MythVideoProfile *Profile)
298{
299 auto autofieldorder = m_autoFieldOrder;
300 auto lastfieldchange = m_lastFieldChange;
301 Cleanup();
302 m_source = nullptr;
303 m_sink = nullptr;
304
305 if (!Frame)
306 return false;
307
308 m_width = Frame->m_width;
309 m_height = Frame->m_height;
310 m_inputType = Frame->m_type;
312 auto name = MythVideoFrame::DeinterlacerName(Deinterlacer | DEINT_CPU, DoubleRate);
313
314 // simple onefield/bob?
315 if (Deinterlacer == DEINT_BASIC || Deinterlacer == DEINT_MEDIUM)
316 {
317 m_deintType = Deinterlacer;
318 m_doubleRate = DoubleRate;
319 m_topFirst = TopFieldFirst;
320 if (Deinterlacer == DEINT_BASIC)
321 {
322 m_swsContext = sws_getCachedContext(m_swsContext, m_width, m_height >> 1, m_inputFmt,
323 m_width, m_height, m_inputFmt, SWS_FAST_BILINEAR,
324 nullptr, nullptr, nullptr);
325 if (m_swsContext == nullptr)
326 return false;
327 }
328 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Using deinterlacer '%1'").arg(name));
329 return true;
330 }
331
332 // Sanity check the frame formats
334 {
335 LOG(VB_GENERAL, LOG_ERR, LOC + "Inconsistent frame formats");
336 return false;
337 }
338
339 m_graph = avfilter_graph_alloc();
340 if (!m_graph)
341 return false;
342
343 uint threads = 1;
344 if (Profile)
345 {
346 threads = std::clamp(Profile->GetMaxCPUs(), 1U, std::max(8U, std::thread::hardware_concurrency()));
347 }
348
349 AVFilterInOut* inputs = nullptr;
350 AVFilterInOut* outputs = nullptr;
351
352 int parity {1};
354 parity = -1;
355 else if (TopFieldFirst)
356 parity = 0;
357 auto deint = QString("yadif=mode=%1:parity=%2:threads=%3")
358 .arg(DoubleRate ? 1 : 0).arg(parity).arg(threads);
359
360 auto graph = QString("buffer=video_size=%1x%2:pix_fmt=%3:time_base=1/1[in];[in]%4[out];[out] buffersink")
361 .arg(m_width).arg(m_height).arg(m_inputFmt).arg(deint);
362
363 int res = avfilter_graph_parse2(m_graph, graph.toLatin1().constData(), &inputs, &outputs);
364 if (res >= 0 && !inputs && !outputs)
365 {
366 res = avfilter_graph_config(m_graph, nullptr);
367 if (res >= 0)
368 {
369 m_source = avfilter_graph_get_filter(m_graph, "Parsed_buffer_0");
370 m_sink = avfilter_graph_get_filter(m_graph, "Parsed_buffersink_2");
371
372 if (m_source && m_sink)
373 {
374 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created deinterlacer '%1' (%2 threads)")
375 .arg(name).arg(threads));
376 m_deintType = Deinterlacer;
377 m_doubleRate = DoubleRate;
378 m_topFirst = TopFieldFirst;
379 m_autoFieldOrder = autofieldorder;
380 m_lastFieldChange = lastfieldchange;
381 return true;
382 }
383 }
384 }
385
386 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create avfilter");
388 avfilter_inout_free(&inputs);
389 avfilter_inout_free(&outputs);
390 return false;
391}
392
394{
395 if (!Frame)
396 return false;
397
398 if (m_bobFrame && !((m_bobFrame->m_bufferSize == Frame->m_bufferSize)) && (m_bobFrame->m_width == Frame->m_width) &&
399 (m_bobFrame->m_height == Frame->m_height) && (m_bobFrame->m_type == Frame->m_type))
400 {
401 delete m_bobFrame;
402 m_bobFrame = nullptr;
403 }
404
405 if (!m_bobFrame)
406 {
408 Frame->m_bufferSize, Frame->m_width, Frame->m_height);
409 LOG(VB_PLAYBACK, LOG_INFO, "Created new 'bob' cache frame");
410 }
411
412 return m_bobFrame && m_bobFrame->m_buffer != nullptr;
413}
414
416{
417 if (!m_swsContext)
418 return;
419
420 // we need a frame for caching - both to preserve the second field if
421 // needed and ensure we are not filtering in place (i.e. from src to src).
422 if (!SetUpCache(Frame))
423 return;
424
425 // copy/cache on first pass
426 if (kScan_Interlaced == Scan)
427 memcpy(m_bobFrame->m_buffer, Frame->m_buffer, m_bobFrame->m_bufferSize);
428
429 // Convert VideoFrame to AVFrame - no copy
430 AVFrame dstframe;
432 (MythAVUtil::FillAVFrame(&dstframe, Frame, m_inputFmt) < 1))
433 {
434 LOG(VB_GENERAL, LOG_ERR, LOC + "Error converting frame");
435 return;
436 }
437
438 bool topfield = Scan == kScan_Interlaced ? m_topFirst : !m_topFirst;
439 dstframe.width = Frame->m_width;
440 dstframe.height = Frame->m_height;
441 dstframe.format = Frame->m_pixFmt;
442
443 m_frame->width = m_bobFrame->m_width;
444 m_frame->format = m_bobFrame->m_pixFmt;
445
446 // Fake the frame height and stride to simulate a single field
447 m_frame->height = Frame->m_height >> 1;
448 m_frame->flags &= ~AV_FRAME_FLAG_INTERLACED;
450 for (uint i = 0; i < nbplanes; i++)
451 {
452 if (!topfield)
453 m_frame->data[i] = m_frame->data[i] + m_frame->linesize[i];
454 m_frame->linesize[i] = m_frame->linesize[i] << 1;
455 }
456
457 // and scale to full height
458 int result = sws_scale(m_swsContext, m_frame->data, m_frame->linesize, 0, m_frame->height,
459 dstframe.data, dstframe.linesize);
460
461 if (result != Frame->m_height)
462 {
463 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Error scaling frame: height %1 expected %2")
464 .arg(result).arg(Frame->m_height));
465 }
466 Frame->m_alreadyDeinterlaced = true;
467}
468
469inline static uint32_t avg(uint32_t A, uint32_t B)
470{
471 return (((A ^ B) & 0xFEFEFEFEUL) >> 1) + (A & B);
472}
473
474// Optimised version with 4x4 alignment
475static inline void BlendC4x4(unsigned char *Src, int Width, int FirstRow, int LastRow, int Pitch,
476 unsigned char *Dst, int DstPitch, bool Second)
477{
478 int srcpitch = Pitch << 1;
479 int dstpitch = DstPitch << 1;
480 int maxrows = LastRow - 3;
481
482 unsigned char *above = Src + ((FirstRow - 1) * static_cast<ptrdiff_t>(Pitch));
483 unsigned char *dest1 = Dst + (FirstRow * static_cast<ptrdiff_t>(DstPitch));
484 unsigned char *middle = above + srcpitch;
485 unsigned char *dest2 = dest1 + dstpitch;
486 unsigned char *below = middle + srcpitch;
487 unsigned char *dstcpy1 = Dst + ((FirstRow - 1) * static_cast<ptrdiff_t>(DstPitch));
488 unsigned char *dstcpy2 = dstcpy1 + dstpitch;
489
490 srcpitch <<= 1;
491 dstpitch <<= 1;
492
493 // 4 rows per pass
494 for (int row = FirstRow; row < maxrows; row += 4)
495 {
496 if (Second)
497 {
498 // On second pass, copy over the original, current field
499 memcpy(dstcpy1, above, static_cast<size_t>(DstPitch));
500 memcpy(dstcpy2, middle, static_cast<size_t>(DstPitch));
501 dstcpy1 += dstpitch;
502 dstcpy2 += dstpitch;
503 }
504 for (int col = 0; col < Width; col += 4)
505 {
506 *reinterpret_cast<uint32_t*>(&dest1[col]) =
507 avg(*reinterpret_cast<uint32_t*>(&above[col]), *reinterpret_cast<uint32_t*>(&middle[col]));
508 *reinterpret_cast<uint32_t*>(&dest2[col]) =
509 avg(*reinterpret_cast<uint32_t*>(&middle[col]), *reinterpret_cast<uint32_t*>(&below[col]));
510 }
511 above += srcpitch;
512 middle += srcpitch;
513 below += srcpitch;
514 dest1 += dstpitch;
515 dest2 += dstpitch;
516 }
517}
518
519#if defined(Q_PROCESSOR_X86_64) || HAVE_INTRINSICS_NEON
520// SIMD optimised version with 16x4 alignment
521static inline void BlendSIMD16x4(unsigned char *Src, int Width, int FirstRow, int LastRow, int Pitch,
522 unsigned char *Dst, int DstPitch, bool Second)
523{
524 int srcpitch = Pitch << 1;
525 int dstpitch = DstPitch << 1;
526 int maxrows = LastRow - 3;
527
528 unsigned char *above = Src + ((FirstRow - 1) * static_cast<ptrdiff_t>(Pitch));
529 unsigned char *dest1 = Dst + (FirstRow * static_cast<ptrdiff_t>(DstPitch));
530 unsigned char *middle = above + srcpitch;
531 unsigned char *dest2 = dest1 + dstpitch;
532 unsigned char *below = middle + srcpitch;
533 unsigned char *dstcpy1 = Dst + ((FirstRow - 1) * static_cast<ptrdiff_t>(DstPitch));
534 unsigned char *dstcpy2 = dstcpy1 + dstpitch;
535
536 srcpitch <<= 1;
537 dstpitch <<= 1;
538
539 // 4 rows per pass
540 for (int row = FirstRow; row < maxrows; row += 4)
541 {
542 if (Second)
543 {
544 // On second pass, copy over the original, current field
545 memcpy(dstcpy1, above, static_cast<size_t>(DstPitch));
546 memcpy(dstcpy2, middle, static_cast<size_t>(DstPitch));
547 dstcpy1 += dstpitch;
548 dstcpy2 += dstpitch;
549 }
550 for (int col = 0; col < Width; col += 16)
551 {
552#if defined(Q_PROCESSOR_X86_64)
553 __m128i mid = *reinterpret_cast<__m128i*>(&middle[col]);
554 *reinterpret_cast<__m128i*>(&dest1[col]) =
555 _mm_avg_epu8(*reinterpret_cast<__m128i*>(&above[col]), mid);
556 *reinterpret_cast<__m128i*>(&dest2[col]) =
557 _mm_avg_epu8(*reinterpret_cast<__m128i*>(&below[col]), mid);
558#endif
559#if HAVE_INTRINSICS_NEON
560 uint8x16_t mid = *reinterpret_cast<uint8x16_t*>(&middle[col]);
561 *reinterpret_cast<uint8x16_t*>(&dest1[col]) =
562 vrhaddq_u8(*reinterpret_cast<uint8x16_t*>(&above[col]), mid);
563 *reinterpret_cast<uint8x16_t*>(&dest2[col]) =
564 vrhaddq_u8(*reinterpret_cast<uint8x16_t*>(&below[col]), mid);
565#endif
566 }
567 above += srcpitch;
568 middle += srcpitch;
569 below += srcpitch;
570 dest1 += dstpitch;
571 dest2 += dstpitch;
572 }
573}
574
575// SIMD optimised version with 16x4 alignment for 10/12/16bit video
576static inline void BlendSIMD8x4(unsigned char *Src, int Width, int FirstRow, int LastRow, int Pitch,
577 unsigned char *Dst, int DstPitch, bool Second)
578{
579 int srcpitch = Pitch << 1;
580 int dstpitch = DstPitch << 1;
581 int maxrows = LastRow - 3;
582
583 unsigned char *above = Src + ((FirstRow - 1) * static_cast<ptrdiff_t>(Pitch));
584 unsigned char *dest1 = Dst + (FirstRow * static_cast<ptrdiff_t>(DstPitch));
585 unsigned char *middle = above + srcpitch;
586 unsigned char *dest2 = dest1 + dstpitch;
587 unsigned char *below = middle + srcpitch;
588 unsigned char *dstcpy1 = Dst + ((FirstRow - 1) * static_cast<ptrdiff_t>(DstPitch));
589 unsigned char *dstcpy2 = dstcpy1 + dstpitch;
590
591 srcpitch <<= 1;
592 dstpitch <<= 1;
593
594 // 4 rows per pass
595 for (int row = FirstRow; row < maxrows; row += 4)
596 {
597 if (Second)
598 {
599 // On second pass, copy over the original, current field
600 memcpy(dstcpy1, above, static_cast<size_t>(DstPitch));
601 memcpy(dstcpy2, middle, static_cast<size_t>(DstPitch));
602 dstcpy1 += dstpitch;
603 dstcpy2 += dstpitch;
604 }
605 for (int col = 0; col < Width; col += 16)
606 {
607#if defined(Q_PROCESSOR_X86_64)
608 __m128i mid = *reinterpret_cast<__m128i*>(&middle[col]);
609 *reinterpret_cast<__m128i*>(&dest1[col]) =
610 _mm_avg_epu16(*reinterpret_cast<__m128i*>(&above[col]), mid);
611 *reinterpret_cast<__m128i*>(&dest2[col]) =
612 _mm_avg_epu16(*reinterpret_cast<__m128i*>(&below[col]), mid);
613#endif
614#if HAVE_INTRINSICS_NEON
615 uint16x8_t mid = *reinterpret_cast<uint16x8_t*>(&middle[col]);
616 *reinterpret_cast<uint16x8_t*>(&dest1[col]) =
617 vrhaddq_u16(*reinterpret_cast<uint16x8_t*>(&above[col]), mid);
618 *reinterpret_cast<uint16x8_t*>(&dest2[col]) =
619 vrhaddq_u16(*reinterpret_cast<uint16x8_t*>(&below[col]), mid);
620#endif
621 }
622 above += srcpitch;
623 middle += srcpitch;
624 below += srcpitch;
625 dest1 += dstpitch;
626 dest2 += dstpitch;
627 }
628}
629#endif
630
632{
633 if (Frame->m_height < 16 || Frame->m_width < 16)
634 return;
635
636 bool second = false;
637 MythVideoFrame *src = Frame;
638
639 if (m_doubleRate)
640 {
641 if (!SetUpCache(Frame))
642 return;
643 // copy/cache on first pass.
644 if (kScan_Interlaced == Scan)
645 memcpy(m_bobFrame->m_buffer, Frame->m_buffer, m_bobFrame->m_bufferSize);
646 else
647 second = true;
648 src = m_bobFrame;
649 }
650
651 bool hidepth = MythVideoFrame::ColorDepth(src->m_type) > 8;
652 bool top = second ? !m_topFirst : m_topFirst;
654 for (uint plane = 0; plane < count; plane++)
655 {
656 int height = MythVideoFrame::GetHeightForPlane(src->m_type, src->m_height, plane);
657 int firstrow = top ? 1 : 2;
658 bool height4 = (height % 4) == 0;
659 bool width4 = (src->m_pitches[plane] % 4) == 0;
660 // N.B. all frames allocated by MythTV should have 16 byte alignment
661 // for all planes
662#if defined(Q_PROCESSOR_X86_64) || HAVE_INTRINSICS_NEON
663 bool width16 = (src->m_pitches[plane] % 16) == 0;
664 // profiling SSE2 suggests it is usually 4x faster - as expected
665 if (s_haveSIMD && height4 && width16)
666 {
667 if (hidepth)
668 {
669 BlendSIMD8x4(src->m_buffer + src->m_offsets[plane],
671 firstrow, height, src->m_pitches[plane],
672 Frame->m_buffer + Frame->m_offsets[plane], Frame->m_pitches[plane],
673 second);
674 }
675 else
676 {
677 BlendSIMD16x4(src->m_buffer + src->m_offsets[plane],
679 firstrow, height, src->m_pitches[plane],
680 Frame->m_buffer + Frame->m_offsets[plane], Frame->m_pitches[plane],
681 second);
682 }
683 }
684 else
685#endif
686 // N.B. There is no 10bit support here - but it shouldn't be necessary
687 // as everything should be 16byte aligned and 10/12bit interlaced video
688 // is virtually unheard of.
689 if (width4 && height4 && !hidepth)
690 {
691 BlendC4x4(src->m_buffer + src->m_offsets[plane],
693 firstrow, height, src->m_pitches[plane],
694 Frame->m_buffer + Frame->m_offsets[plane], Frame->m_pitches[plane],
695 second);
696 }
697 }
698 Frame->m_alreadyDeinterlaced = true;
699}
AVFrame AVFrame
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
Definition: mythavutil.cpp:72
static int FillAVFrame(AVFrame *Frame, const MythVideoFrame *From, AVPixelFormat Fmt=AV_PIX_FMT_NONE)
Initialise AVFrame with content from MythVideoFrame.
Definition: mythavutil.cpp:199
static AVPixelFormat FrameTypeToPixelFormat(VideoFrameType Type)
Definition: mythavutil.cpp:28
void OneField(MythVideoFrame *Frame, FrameScanType Scan)
bool Initialise(MythVideoFrame *Frame, MythDeintType Deinterlacer, bool DoubleRate, bool TopFieldFirst, MythVideoProfile *Profile)
Initialise deinterlacing using the given MythDeintType.
AVPixelFormat m_inputFmt
void Filter(MythVideoFrame *Frame, FrameScanType Scan, MythVideoProfile *Profile, bool Force=false)
Deinterlace Frame if needed.
VideoFrameType m_inputType
AVFilterGraph * m_graph
MythDeintType m_deintType
uint64_t m_lastFieldChange
AVFilterContext * m_sink
void Blend(MythVideoFrame *Frame, FrameScanType Scan)
uint64_t m_discontinuityCounter
AVFilterContext * m_source
MythVideoFrame * m_bobFrame
bool SetUpCache(MythVideoFrame *Frame)
SwsContext * m_swsContext
MythAVFrame m_frame
VideoFrameType m_type
Definition: mythframe.h:118
static uint GetNumPlanes(VideoFrameType Type)
Definition: mythframe.h:213
static int GetPitchForPlane(VideoFrameType Type, int Width, uint Plane)
Definition: mythframe.h:303
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
Definition: mythframe.cpp:462
static bool FormatIsNV12(VideoFrameType Type)
Definition: mythframe.h:455
static QString FormatDescription(VideoFrameType Type)
Definition: mythframe.cpp:368
size_t m_bufferSize
Definition: mythframe.h:123
static int GetHeightForPlane(VideoFrameType Type, int Height, uint Plane)
Definition: mythframe.h:257
FramePitches m_pitches
Definition: mythframe.h:141
uint8_t * m_buffer
Definition: mythframe.h:119
static uint8_t * GetAlignedBuffer(size_t Size)
Definition: mythframe.cpp:430
static bool YUVFormat(VideoFrameType Type)
Definition: mythframe.h:465
static void CopyPlane(uint8_t *To, int ToPitch, const uint8_t *From, int FromPitch, int PlaneWidth, int PlaneHeight)
Definition: mythframe.cpp:188
static int ColorDepth(int Format)
Definition: mythframe.h:398
static int GetWidthForPlane(VideoFrameType Type, int Width, uint Plane)
Definition: mythframe.h:353
FrameOffsets m_offsets
Definition: mythframe.h:142
uint GetMaxCPUs() const
unsigned int uint
Definition: freesurround.h:24
#define LOC
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)
MythDeintType
Definition: mythframe.h:67
@ DEINT_HIGH
Definition: mythframe.h:71
@ DEINT_MEDIUM
Definition: mythframe.h:70
@ DEINT_BASIC
Definition: mythframe.h:69
@ DEINT_NONE
Definition: mythframe.h:68
@ DEINT_SHADER
Definition: mythframe.h:73
@ DEINT_CPU
Definition: mythframe.h:72
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static QString Source(const QNetworkRequest &request)
Definition: netstream.cpp:139
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
FrameScanType
Definition: videoouttypes.h:95
@ kScan_Interlaced
Definition: videoouttypes.h:98
bool is_interlaced(FrameScanType Scan)