Index: libs/libmythtv/NuppelVideoPlayer.h =================================================================== --- libs/libmythtv/NuppelVideoPlayer.h (revision 9292) +++ libs/libmythtv/NuppelVideoPlayer.h (working copy) @@ -136,7 +136,7 @@ void SetBookmark(void); void SetKeyframeDistance(int keyframedistance); void SetVideoParams(int w, int h, double fps, int keydist, - float a = 1.33333, FrameScanType scan = kScan_Ignore); + float a = 1.33333); void SetAudioParams(int bits, int channels, int samplerate, bool passthru); void SetEffDsp(int dsprate); void SetFileLength(int total, int frames); @@ -144,6 +144,17 @@ void ClearBookmark(void); void SetForcedAspectRatio(int mpeg2_aspect_value, int letterbox_permission); + // functions for user to force deinterlace settings + enum DeintMode { + DeintAuto, + DeintOn, + DeintOff, + DeintUnknown + }; + void CycleDeinterlaceMode(); + void SetDeinterlaceMode(DeintMode mode); + DeintMode GetDeinterlaceMode(); + void SetOSDFontName(const QString osdfonts[22], const QString &prefix); void SetOSDThemeName(const QString themename); @@ -505,7 +516,12 @@ mutable bool limitKeyRepeat; bool errored; int m_DeintSetting; + int frame_scan_cnt; + void EnableDeinterlace(); + void DisableDeinterlace(); + void SetDeinterlaceMode(DeintMode new_mode, DeintMode cur_mode); + // Bookmark stuff long long bookmarkseek; bool previewFromBookmark; @@ -544,6 +560,7 @@ float forced_video_aspect; /// Video (input) Scan Type (interlaced, progressive, detect, ignore...) FrameScanType m_scan; + bool m_scan_locked; /// Video (input) Number of frames between key frames (often inaccurate) int keyframedist; Index: libs/libmythtv/nuppeldecoder.cpp =================================================================== --- libs/libmythtv/nuppeldecoder.cpp (revision 9292) +++ libs/libmythtv/nuppeldecoder.cpp (working copy) @@ -204,7 +204,7 @@ GetNVP()->SetVideoParams(fileheader.width, fileheader.height, fileheader.fps, fileheader.keyframedist, - fileheader.aspect, kScan_Detect); + fileheader.aspect); video_width = fileheader.width; video_height = fileheader.height; Index: libs/libmythtv/NuppelVideoPlayer.cpp =================================================================== --- libs/libmythtv/NuppelVideoPlayer.cpp (revision 9292) +++ libs/libmythtv/NuppelVideoPlayer.cpp (working copy) @@ -141,6 +141,7 @@ hasFullPositionMap(false), limitKeyRepeat(false), errored(false), m_DeintSetting(0), + frame_scan_cnt(0), // Bookmark stuff bookmarkseek(0), previewFromBookmark(false), // Seek @@ -154,7 +155,8 @@ video_width(0), video_height(0), video_size(0), video_frame_rate(29.97f), video_aspect(4.0f / 3.0f), forced_video_aspect(-1), - m_scan(kScan_Detect), keyframedist(30), + m_scan(kScan_Interlaced), m_scan_locked(false), + keyframedist(30), // RingBuffer stuff filename("output.nuv"), weMadeBuffer(false), ringBuffer(NULL), // Prebuffering (RingBuffer) control @@ -685,9 +687,130 @@ } } +/** \fn NuppelVideoPlayer::EnableDeinterlace(void) + * \brief enable current deinterlacing method + */ +void NuppelVideoPlayer::EnableDeinterlace(void) +{ + m_scan = kScan_Interlaced; + + videoOutput->EnableDeinterlace(); + + if (videoOutput->NeedsDoubleFramerate()) + { + videosync->SetFrameInterval(frame_interval, true); + m_double_framerate = true; + m_can_double = true; + } + + VERBOSE(VB_PLAYBACK, "Enabled deinterlacing"); +} + +/** \fn NuppelVideoPlayer::DisableDeinterlace(void) + * \brief leave current deinterlacing method in place, but disable + */ +void NuppelVideoPlayer::DisableDeinterlace(void) +{ + m_scan = kScan_Progressive; + + if (m_double_framerate) + { + m_double_framerate = false; + m_can_double = false; + videosync->SetFrameInterval(frame_interval, false); + } + videoOutput->DisableDeinterlace(); + + VERBOSE(VB_PLAYBACK, "Disabled deinterlacing"); +} + +void NuppelVideoPlayer::CycleDeinterlaceMode() +{ + // Cycle through autodetect, deinterlace forced on, deinterlace forced off + // + // Autodetect is m_scan_locked == false + // deinterlace on is kScan_Interlaced, m_scan_locked = true + // deinterlace off is kScan_Progressive, m_scan_locked = true + // + if (!m_scan_locked) + { + // previous state autodetect + // + // next state forced on + SetDeinterlaceMode(DeintOn); + } + else if (m_scan == kScan_Interlaced) + { + // previous state forced on + // + // next state forced off + SetDeinterlaceMode(DeintOff); + } + else if (m_scan == kScan_Progressive) + { + // previous state forced off + // + // next state autodetect + SetDeinterlaceMode(DeintAuto); + } + else + { + VERBOSE(VB_IMPORTANT, "Unknown deinterlace state, no change made"); + } +} + +void NuppelVideoPlayer::SetDeinterlaceMode(DeintMode new_mode) +{ + FrameScanType new_scan = kScan_Ignore; + + if (new_mode == DeintAuto) + { + if (frame_scan_cnt > 0) + new_scan = kScan_Interlaced; + if (frame_scan_cnt < 0) + new_scan = kScan_Progressive; + } + else if (new_mode == DeintOn) + new_scan = kScan_Interlaced; + else if (new_mode == DeintOff) + new_scan = kScan_Progressive; + else + return; // shouldn't happen + + videofiltersLock.lock(); + + m_scan_locked = (new_mode != DeintAuto); + + if (new_scan != m_scan) + { + if (new_scan == kScan_Interlaced) + EnableDeinterlace(); + if (new_scan == kScan_Progressive) + DisableDeinterlace(); + } + + videofiltersLock.unlock(); +} + +NuppelVideoPlayer::DeintMode NuppelVideoPlayer::GetDeinterlaceMode() +{ + DeintMode mode = DeintUnknown; + + if (m_scan_locked) + { + if (m_scan == kScan_Interlaced) + mode = DeintOn; + if (m_scan == kScan_Progressive) + mode = DeintOff; + } + else + mode = DeintAuto; + + return mode; +} + void NuppelVideoPlayer::SetVideoParams(int width, int height, double fps, - int keyframedistance, float aspect, - FrameScanType scan) + int keyframedistance, float aspect) { if (width == 0 || height == 0 || isnan(aspect) || isnan(fps)) return; @@ -715,45 +838,6 @@ if (videoOutput) ReinitVideo(); - - if (IsErrored()) - return; - - videofiltersLock.lock(); - - m_scan = detectInterlace(scan, m_scan, video_frame_rate, video_height); - VERBOSE(VB_PLAYBACK, QString("Interlaced: %1 video_height: %2 fps: %3") - .arg(toQString(m_scan)).arg(video_height) - .arg(fps)); - - // Set up deinterlacing in the video output method - m_double_framerate = false; - if (videoOutput) - { - videoOutput->SetupDeinterlace(false); - if ((m_scan == kScan_Interlaced) && - m_DeintSetting && - videoOutput->SetupDeinterlace(true) && - videoOutput->NeedsDoubleFramerate()) - { - m_double_framerate = true; - m_can_double = true; - } - } - - // Make sure video sync can double frame rate - if (videosync && m_double_framerate) - { - videosync->SetFrameInterval(frame_interval, m_double_framerate); - if (videosync->UsesFrameInterval()) - { - VERBOSE(VB_IMPORTANT, "Video sync method can't support double " - "framerate (refresh rate too low for bob deint)"); - FallbackDeint(); - } - } - - videofiltersLock.unlock(); } void NuppelVideoPlayer::SetFileLength(int total, int frames) @@ -2254,7 +2338,40 @@ else if (osdHasSubtitles || nonDisplayedSubtitles.size() > 20) ClearSubtitles(); + if (frame) + { + if (frame->interlaced_frame) + { + if (frame_scan_cnt < 0) + { + VERBOSE(VB_PLAYBACK, LOC + "interlaced frame seen after " << (-1 * frame_scan_cnt) << " progressive frames"); + frame_scan_cnt = 0; + } + frame_scan_cnt += 1; + } + else + { + if (frame_scan_cnt > 0) + { + VERBOSE(VB_PLAYBACK, LOC + "progressive frame seen after " << frame_scan_cnt << " interlaced frames"); + frame_scan_cnt = 0; + } + frame_scan_cnt -= 1; + + } + + if ((frame_scan_cnt % 480) == 0) + { + int temp = (frame_scan_cnt < 0) ? (-1 * frame_scan_cnt) : frame_scan_cnt; + VERBOSE(VB_PLAYBACK, LOC + "" << temp << " " << ((frame_scan_cnt < 0) ? "progressive" : "interlaced") << " frames"); + } + } + videofiltersLock.lock(); + if (!m_scan_locked && m_DeintSetting && (m_scan == kScan_Progressive) && (frame_scan_cnt > 2)) + EnableDeinterlace(); + if (!m_scan_locked && (m_scan == kScan_Interlaced) && (frame_scan_cnt < -2)) + DisableDeinterlace(); videoOutput->ProcessFrame(frame, osd, videoFilters, pipplayer); videofiltersLock.unlock(); @@ -2290,6 +2407,20 @@ if (videoOutput) rf_int = videoOutput->GetRefreshRate(); + // Default to Interlaced playback to allocate the deinterlacer structures + m_scan = kScan_Interlaced; + + // Enable autodetection of interlaced/progressive from video stream + m_scan_locked = false; + + // init to non-Bob + m_double_framerate = false; + m_can_double = false; + + // Init to a value which will cause an immediate switch to + // progressive if the first frame is progressive. + frame_scan_cnt = 2; + if (using_null_videoout) { videosync = new USleepVideoSync(videoOutput, (int)fr_int, 0, false); @@ -2297,8 +2428,8 @@ else if (videoOutput) { // Set up deinterlacing in the video output method - m_double_framerate = false; - if (m_scan == kScan_Interlaced && m_DeintSetting && + if (m_scan == kScan_Interlaced && + m_DeintSetting && videoOutput->SetupDeinterlace(true) && videoOutput->NeedsDoubleFramerate()) { Index: libs/libmythtv/avformatdecoder.cpp =================================================================== --- libs/libmythtv/avformatdecoder.cpp (revision 9292) +++ libs/libmythtv/avformatdecoder.cpp (working copy) @@ -495,7 +495,7 @@ // Skip all the desired number of skipFrames for (;skipFrames > 0 && !ateof; skipFrames--) { - GetFrame(0); + GetFrame(0); if (decoded_video_frame) GetNVP()->DiscardVideoFrame(decoded_video_frame); } @@ -957,7 +957,7 @@ } GetNVP()->SetVideoParams(align_width, align_height, fps, - keyframedist, aspect_ratio, kScan_Detect); + keyframedist, aspect_ratio); } #ifdef USING_XVMC @@ -1783,8 +1783,7 @@ align_dimensions(context, awidth, aheight); GetNVP()->SetVideoParams(awidth, aheight, seqFPS, - keyframedist, aspect, - kScan_Detect); + keyframedist, aspect); current_width = width; current_height = height; Index: libs/libmythtv/tv_play.cpp =================================================================== --- libs/libmythtv/tv_play.cpp (revision 9292) +++ libs/libmythtv/tv_play.cpp (working copy) @@ -185,6 +185,7 @@ REG_KEY("TV Playback", "JUMPREC", "Display menu of recorded programs to jump to", ""); REG_KEY("TV Playback", "SIGNALMON", "Monitor Signal Quality", "F7"); REG_KEY("TV Playback", "JUMPTODVDROOTMENU", "Jump to the DVD Root Menu", ""); + REG_KEY("TV Playback", "CYCLEDEINTERLACE", "Cycle deinterlacing state through auto, on, off", ""); REG_KEY("TV Editing", "CLEARMAP", "Clear editing cut points", "C,Q,Home"); REG_KEY("TV Editing", "INVERTMAP", "Invert Begin/End cut points", "I"); @@ -2242,6 +2243,8 @@ DoTogglePictureAttribute(); } } + else if (action == "CYCLEDEINTERLACE") + nvp->CycleDeinterlaceMode(); else if (action == "ARBSEEK") { if (asInputMode) @@ -5691,6 +5694,12 @@ ChangeTimeStretch(0, !floatRead); // just display } + else if (action == "DEINTERLACEAUTO") + nvp->SetDeinterlaceMode(NuppelVideoPlayer::DeintAuto); + else if (action == "DEINTERLACEON") + nvp->SetDeinterlaceMode(NuppelVideoPlayer::DeintOn); + else if (action == "DEINTERLACEOFF") + nvp->SetDeinterlaceMode(NuppelVideoPlayer::DeintOff); else if (action.left(15) == "TOGGLEAUDIOSYNC") ChangeAudioSync(0); else if (action.left(11) == "TOGGLESLEEP") @@ -6045,6 +6054,14 @@ (speedX100 == 150) ? 1 : 0, NULL, "STRETCHGROUP"); + // add deinterlacing mode setting to menu + + NuppelVideoPlayer::DeintMode deint_mode = activenvp ? activenvp->GetDeinterlaceMode() : NuppelVideoPlayer::DeintUnknown; + item = new OSDGenericTree(treeMenu, tr("Deinterlace Mode"), "DEINTERLACEMODE"); + subitem = new OSDGenericTree(item, tr("Auto"), "DEINTERLACEAUTO", (deint_mode == NuppelVideoPlayer::DeintAuto) ? 1 : 0, NULL, "DEINTERLACEGROUP"); + subitem = new OSDGenericTree(item, tr("On"), "DEINTERLACEON", (deint_mode == NuppelVideoPlayer::DeintOn) ? 1 : 0, NULL, "DEINTERLACEGROUP"); + subitem = new OSDGenericTree(item, tr("Off"), "DEINTERLACEOFF", (deint_mode == NuppelVideoPlayer::DeintOff) ? 1 : 0, NULL, "DEINTERLACEGROUP"); + // add sleep items to menu item = new OSDGenericTree(treeMenu, tr("Sleep"), "TOGGLESLEEPON"); Index: libs/libmythtv/videoout_null.cpp =================================================================== --- libs/libmythtv/videoout_null.cpp (revision 9292) +++ libs/libmythtv/videoout_null.cpp (working copy) @@ -109,6 +109,20 @@ return true; } +bool VideoOutputNull::SetupDeinterlace(bool i, const QString& ovrf) +{ + return !i; +} +void VideoOutputNull::EnableDeinterlace() +{ + return; +} +void VideoOutputNull::DisableDeinterlace() +{ + return; +} + + void VideoOutputNull::Exit(void) { if (XJ_started) Index: libs/libmythtv/videooutbase.cpp =================================================================== --- libs/libmythtv/videooutbase.cpp (revision 9292) +++ libs/libmythtv/videooutbase.cpp (working copy) @@ -181,7 +181,8 @@ m_deinterlaceBeforeOSD(true), // Various state variables - embedding(false), needrepaint(false), + embedding(false), + needrepaint(false), needbobrepaint(false), allowpreviewepg(true), framesPlayed(0), errored(false) @@ -279,13 +280,6 @@ bool VideoOutput::SetupDeinterlace(bool interlaced, const QString& overridefilter) { - if (VideoOutputNull *null = dynamic_cast(this)) - { - (void)null; - // null vidout doesn't deinterlace - return !interlaced; - } - if (m_deinterlacing == interlaced) return m_deinterlacing; @@ -343,6 +337,42 @@ } /** + * \fn VideoOutput::EnableDeinterlace() + * \brief Attempts to enable deinterlacing with existing deinterlace method. + */ +void VideoOutput::EnableDeinterlace() +{ + if (m_deinterlacing) + return; + + // if no deinterlacer allocated, attempt allocate one + if (!m_deintFiltMan || !m_deintFilter) + { + (void)SetupDeinterlace(true); + return; + } + + m_deinterlacing = true; + + if (m_deintfiltername == "bobdeint") + m_deinterlaceBeforeOSD = false; + else + m_deinterlaceBeforeOSD = true; + + return; +} + +/** + * \fn VideoOutput::DisableDeinterlace() + * \brief Disables deinterlacing without deallocating existing deinterlace method. + */ +void VideoOutput::DisableDeinterlace() +{ + m_deinterlacing = false; + return; +} + +/** * \fn VideoOutput::NeedsDoubleFramerate() const * \brief Should Prepare() and Show() be called twice for every ProcessFrame(). * Index: libs/libmythtv/videoout_xv.h =================================================================== --- libs/libmythtv/videoout_xv.h (revision 9292) +++ libs/libmythtv/videoout_xv.h (working copy) @@ -51,6 +51,8 @@ int winx, int winy, int winw, int winh, WId embedid = 0); bool SetupDeinterlace(bool interlaced, const QString& ovrf=""); bool ApproveDeintFilter(const QString& filtername) const; + void EnableDeinterlace(); + void DisableDeinterlace(); void ProcessFrame(VideoFrame *frame, OSD *osd, FilterChain *filterList, Index: libs/libmythtv/videoout_xv.cpp =================================================================== --- libs/libmythtv/videoout_xv.cpp (revision 9292) +++ libs/libmythtv/videoout_xv.cpp (working copy) @@ -1383,6 +1383,19 @@ return deint; } +void VideoOutputXv::EnableDeinterlace() +{ + VideoOutput::EnableDeinterlace(); + return; +} + +void VideoOutputXv::DisableDeinterlace() +{ + VideoOutput::DisableDeinterlace(); + needbobrepaint = (m_deintfiltername == "bobdeint"); + return; +} + /** * \fn VideoOutput::NeedsDoubleFramerate() const * Approves bobdeint filter for XVideo and XvMC surfaces, @@ -2469,7 +2482,7 @@ return; } - if (needrepaint && (VideoOutputSubType() >= XVideo)) + if ((needrepaint || needbobrepaint) && (VideoOutputSubType() >= XVideo)) DrawUnusedRects(/* don't do a sync*/false); if (VideoOutputSubType() > XVideo) @@ -2483,10 +2496,10 @@ void VideoOutputXv::DrawUnusedRects(bool sync) { // boboff assumes the smallest interlaced resolution is 480 lines - 5% - int boboff = (int)round(((double)disphoff) / 456 - 0.00001); - boboff = (m_deinterlacing && m_deintfiltername == "bobdeint") ? boboff : 0; + int boboff_raw = (int)round(((double)disphoff) / 456 - 0.00001); + int boboff = (m_deinterlacing && m_deintfiltername == "bobdeint") ? boboff_raw : 0; - if (chroma_osd && chroma_osd->GetImage() && needrepaint) + if (chroma_osd && chroma_osd->GetImage() && (needrepaint || needbobrepaint/*needed?*/)) { X11L; XShmPutImage(XJ_disp, XJ_curwin, XJ_gc, chroma_osd->GetImage(), @@ -2496,17 +2509,30 @@ X11U; needrepaint = false; + needbobrepaint = false; return; } X11L; - if (xv_draw_colorkey && needrepaint) + if (xv_draw_colorkey && (needrepaint || needbobrepaint)) { XSetForeground(XJ_disp, XJ_gc, xv_colorkey); - XFillRectangle(XJ_disp, XJ_curwin, XJ_gc, dispx, - dispy + boboff, dispw, disph - 2 * boboff); + if (needrepaint) + XFillRectangle(XJ_disp, XJ_curwin, XJ_gc, dispx, + dispy + boboff, dispw, disph - 2 * boboff); + else { /* needbobrepaint */ + // we enter here if bobdeinterlacing was just turned off. + // in this case we need to fill the rectangles outside the + // smaller bob region with the colorkey. + XFillRectangle(XJ_disp, XJ_curwin, XJ_gc, dispx, + dispy, dispw, boboff_raw); + XFillRectangle(XJ_disp, XJ_curwin, XJ_gc, dispx, + disph - 2 * boboff_raw, dispw, disph); + } + needrepaint = false; + needbobrepaint = false; } // Draw black in masked areas Index: libs/libmythtv/videooutbase.h =================================================================== --- libs/libmythtv/videooutbase.h (revision 9292) +++ libs/libmythtv/videooutbase.h (working copy) @@ -132,6 +132,8 @@ virtual bool SetupDeinterlace(bool i, const QString& ovrf=""); virtual bool NeedsDoubleFramerate(void) const; virtual bool ApproveDeintFilter(const QString& filtername) const; + virtual void EnableDeinterlace(); + virtual void DisableDeinterlace(); virtual void PrepareFrame(VideoFrame *buffer, FrameScanType) = 0; virtual void Show(FrameScanType) = 0; @@ -363,6 +365,7 @@ // Various state variables bool embedding; bool needrepaint; + bool needbobrepaint; bool allowpreviewepg; long long framesPlayed; Index: libs/libmythtv/videoout_null.h =================================================================== --- libs/libmythtv/videoout_null.h (revision 9292) +++ libs/libmythtv/videoout_null.h (working copy) @@ -11,6 +11,11 @@ bool Init(int width, int height, float aspect, WId winid, int winx, int winy, int winw, int winh, WId embedid = 0); + + bool SetupDeinterlace(bool i, const QString& ovrf=""); + void EnableDeinterlace(); + void DisableDeinterlace(); + void PrepareFrame(VideoFrame *buffer, FrameScanType); void Show(FrameScanType );