From 9609a512bdb152f89e086c89dcbade4a9becb5d2 Mon Sep 17 00:00:00 2001 From: Mark Kendall Date: Sun, 9 Jun 2019 23:47:01 +0100 Subject: [PATCH] Improved interlaced detection - the basic premise here is that we don't want to setup deinterlacers unnecessarily (to save resources and avoid confusion in the logs etc) and that FFmpeg is pretty good these days at getting interlaced flags correct - so don't use detection based on size/frame rate on stream changes, set the initial state as interlaced but with the scan tracker as progressive (if that makes sense). The scan is immediately corrected on detection of a progressive frame and no deinterlacers are created. We set to interlaced only to account for those cases where the frame rate is detected incorrectly on startup (usually 29.97 as 59.98) and we initially think the file is progressive. - testing with my 'test suite' of curious files and formats, this gets it correct for all but 1 of about 120 files - which is better than NVDECs detection. The one misdetected file is clearly an interlaced NTSC stream that is not flagged as such - all playes get it wrong. NVDEC doubles the frame rate but doesn't deinterlace it. --- .../libmythtv/decoders/avformatdecoder.cpp | 32 +++++++++++-------- .../libs/libmythtv/mythmediacodeccontext.cpp | 2 +- mythtv/libs/libmythtv/mythnvdeccontext.cpp | 8 +++++ mythtv/libs/libmythtv/mythplayer.cpp | 20 +++++++----- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/mythtv/libs/libmythtv/decoders/avformatdecoder.cpp b/mythtv/libs/libmythtv/decoders/avformatdecoder.cpp index 247efacb911..76a7f8b6325 100644 --- a/mythtv/libs/libmythtv/decoders/avformatdecoder.cpp +++ b/mythtv/libs/libmythtv/decoders/avformatdecoder.cpp @@ -1429,8 +1429,10 @@ float AvFormatDecoder::normalized_fps(AVStream *stream, AVCodecContext *enc) fps = codec_fps; else if (container_fps < 121.0 && container_fps > 3.0) fps = container_fps; + // certain H.264 interlaced streams are detected at 2x using estimated (i.e. wrong) else if (estimated_fps < 121.0 && estimated_fps > 3.0) fps = estimated_fps; + // but average is less reliable as it does not account for issues like repeat frames else if (avg_fps < 121.0 && avg_fps > 3.0) fps = avg_fps; else @@ -3268,13 +3270,13 @@ void AvFormatDecoder::MpegPreProcessPkt(AVStream *stream, AVPacket *pkt) SequenceHeader *seq = reinterpret_cast( const_cast(bufptr)); - uint width = seq->width() >> context->lowres; - uint height = seq->height() >> context->lowres; + int width = static_cast(seq->width()) >> context->lowres; + int height = static_cast(seq->height()) >> context->lowres; m_current_aspect = seq->aspect(context->codec_id == AV_CODEC_ID_MPEG1VIDEO); if (stream->sample_aspect_ratio.num) - m_current_aspect = av_q2d(stream->sample_aspect_ratio) * - width / height; + m_current_aspect = static_cast(av_q2d(stream->sample_aspect_ratio) * + width / height); if (aspect_override > 0.0F) m_current_aspect = aspect_override; float seqFPS = seq->fps(); @@ -3282,19 +3284,21 @@ void AvFormatDecoder::MpegPreProcessPkt(AVStream *stream, AVPacket *pkt) bool changed = (seqFPS > static_cast(m_fps)+0.01F) || (seqFPS < static_cast(m_fps)-0.01F); - changed |= (width != (uint)m_current_width ); - changed |= (height != (uint)m_current_height); + changed |= (width != m_current_width ); + changed |= (height != m_current_height); if (changed) { if (m_private_dec) m_private_dec->Reset(); + // N.B. we now set the default scan to kScan_Ignore as interlaced detection based on frame + // size and rate is extremely error prone and FFmpeg gets it right far more often. // as for H.264, if a decoder deinterlacer is in operation - the stream must be progressive bool doublerate = false; bool decoderdeint = m_mythcodecctx->IsDeinterlacing(doublerate); - m_parent->SetVideoParams(width, height, seqFPS, m_current_aspect, - decoderdeint ? kScan_Progressive : kScan_Detect); + m_parent->SetVideoParams(width, height, static_cast(seqFPS), m_current_aspect, + decoderdeint ? kScan_Progressive : kScan_Ignore); m_current_width = width; m_current_height = height; @@ -3314,9 +3318,8 @@ void AvFormatDecoder::MpegPreProcessPkt(AVStream *stream, AVPacket *pkt) float avFPS = normalized_fps(stream, context); if ((seqFPS > avFPS+0.01F) || (seqFPS < avFPS-0.01F)) { - LOG(VB_PLAYBACK, LOG_INFO, LOC + - QString("avFPS(%1) != seqFPS(%2)") - .arg(avFPS).arg(seqFPS)); + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("avFPS(%1) != seqFPS(%2)") + .arg(static_cast(avFPS)).arg(static_cast(seqFPS))); } } @@ -3365,7 +3368,7 @@ int AvFormatDecoder::H264PreProcessPkt(AVStream *stream, AVPacket *pkt) while (buf < buf_end) { - buf += m_h264_parser->addBytes(buf, buf_end - buf, 0); + buf += m_h264_parser->addBytes(buf, static_cast(buf_end - buf), 0); if (m_h264_parser->stateChanged()) { @@ -3401,10 +3404,13 @@ int AvFormatDecoder::H264PreProcessPkt(AVStream *stream, AVPacket *pkt) if (m_private_dec) m_private_dec->Reset(); + // N.B. we now set the default scan to kScan_Ignore as interlaced detection based on frame + // size and rate is extremely error prone and FFmpeg gets it right far more often. // N.B. if a decoder deinterlacer is in use - the stream must be progressive bool doublerate = false; bool decoderdeint = m_mythcodecctx->IsDeinterlacing(doublerate); - m_parent->SetVideoParams(width, height, seqFPS, m_current_aspect, decoderdeint ? kScan_Progressive : kScan_Detect); + m_parent->SetVideoParams(width, height, seqFPS, m_current_aspect, + decoderdeint ? kScan_Progressive : kScan_Ignore); m_current_width = width; m_current_height = height; diff --git a/mythtv/libs/libmythtv/mythmediacodeccontext.cpp b/mythtv/libs/libmythtv/mythmediacodeccontext.cpp index 5a71fc61869..2fb9cca97dd 100644 --- a/mythtv/libs/libmythtv/mythmediacodeccontext.cpp +++ b/mythtv/libs/libmythtv/mythmediacodeccontext.cpp @@ -120,7 +120,7 @@ void MythMediaCodecContext::PostProcessFrame(AVCodecContext*, VideoFrame* Frame) if (!Frame) return; - Frame->deinterlace_inuse = Frame->interlaced_frame ? (DEINT_BASIC | DEINT_DRIVER) : DEINT_NONE; + Frame->deinterlace_inuse = DEINT_BASIC | DEINT_DRIVER; Frame->deinterlace_inuse2x = 0; Frame->interlaced_frame = 0; Frame->interlaced_reversed = 0; diff --git a/mythtv/libs/libmythtv/mythnvdeccontext.cpp b/mythtv/libs/libmythtv/mythnvdeccontext.cpp index 6005cfa3a9a..e073db040ce 100644 --- a/mythtv/libs/libmythtv/mythnvdeccontext.cpp +++ b/mythtv/libs/libmythtv/mythnvdeccontext.cpp @@ -230,6 +230,14 @@ void MythNVDECContext::SetDeinterlacing(AVCodecContext *Context, if (!Context) return; + // Don't enable for anything that cannot be interlaced + // We could use frame rate here but initial frame rate detection is not always accurate + // and we lose little by enabling deinterlacing. NVDEC will not deinterlace a + // progressive stream and any CUDA capable video card has memory to spare + // (assuming it even sets up deinterlacing for a progressive stream) + if (Context->height == 720) // 720P + return; + MythDeintType deinterlacer = DEINT_NONE; MythDeintType singlepref = DEINT_HIGH | DEINT_DRIVER; MythDeintType doublepref = DoubleRate ? DEINT_HIGH | DEINT_DRIVER : DEINT_NONE; diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp index 6af72019663..fdfa2028de5 100644 --- a/mythtv/libs/libmythtv/mythplayer.cpp +++ b/mythtv/libs/libmythtv/mythplayer.cpp @@ -644,7 +644,7 @@ FrameScanType MythPlayer::detectInterlace(FrameScanType newScan, { QString dbg = QString("detectInterlace(") + toQString(newScan) + QString(", ") + toQString(scan) + QString(", ") + - QString("%1").arg(fps) + QString(", ") + + QString("%1").arg(static_cast(fps)) + QString(", ") + QString("%1").arg(video_height) + QString(") ->"); if (kScan_Ignore != newScan || kScan_Detect == scan) @@ -662,7 +662,7 @@ FrameScanType MythPlayer::detectInterlace(FrameScanType newScan, scan = newScan; }; - LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg+toQString(scan)); + LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg +toQString(scan)); return scan; } @@ -682,7 +682,7 @@ void MythPlayer::AutoDeint(VideoFrame *frame, bool allow_lock) if (m_scan_tracker < 0) { LOG(VB_PLAYBACK, LOG_INFO, LOC + - QString("interlaced frame seen after %1 progressive frames") + QString("Interlaced frame seen after %1 progressive frames") .arg(abs(m_scan_tracker))); m_scan_tracker = 2; if (allow_lock) @@ -699,7 +699,7 @@ void MythPlayer::AutoDeint(VideoFrame *frame, bool allow_lock) if (m_scan_tracker > 0) { LOG(VB_PLAYBACK, LOG_INFO, LOC + - QString("progressive frame seen after %1 interlaced frames") + QString("Progressive frame seen after %1 interlaced frames") .arg(m_scan_tracker)); m_scan_tracker = 0; } @@ -2661,14 +2661,18 @@ void MythPlayer::VideoStart(void) int fr_int = (1000000.0 / video_frame_rate / static_cast(temp_speed)); int rf_int = MythDisplay::GetDisplayInfo(fr_int).Rate(); - // Default to Interlaced playback to allocate the deinterlacer structures + // Default to interlaced playback but set the tracker to progressive // Enable autodetection of interlaced/progressive from video stream - // And initialoze m_scan_tracker to 2 which will immediately switch to - // progressive if the first frame is progressive in AutoDeint(). + // Previously we set to interlaced and the scan tracker to 2 but this + // mis-'detected' a number of streams as interlaced when they are progressive. + // This significantly reduces the number of errors and also ensures we do not + // needlessly setup deinterlacers - which may consume significant resources. + // We set to interlaced for those streams whose frame rate is initially detected + // as e.g. 59.9 when it is actually 29.97 interlaced. m_scan = kScan_Interlaced; m_scan_locked = false; m_double_framerate = false; - m_scan_tracker = 2; + m_scan_tracker = -2; if (player_ctx->IsPIP() && FlagIsSet(kVideoIsNull)) {