diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 9fb3dcb2b0627..f5e17dbfc8465 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -23789,6 +23789,12 @@ msgctxt "#39201" msgid "HDR10+" msgstr "" +#: xbmc/dialogs/GUIDialogSubtitles.cpp +msgctxt "#39202" +msgid "Fix 24/25 fps mismatch" +msgstr "" + + # 40000 to 40800 are reserved for Video Versions feature #. Generic video versions label (plural) diff --git a/xbmc/application/ApplicationPlayer.cpp b/xbmc/application/ApplicationPlayer.cpp index 9d14233e2217b..8b514247d3729 100644 --- a/xbmc/application/ApplicationPlayer.cpp +++ b/xbmc/application/ApplicationPlayer.cpp @@ -737,6 +737,21 @@ void CApplicationPlayer::SetSubTitleDelay(float fValue) player->SetSubTitleDelay(fValue); } +void CApplicationPlayer::SetSubtitleCompensateFPS(bool bValue) +{ + std::shared_ptr player = GetInternal(); + if (player) + player->SetSubtitleCompensateFPS(bValue); +} + +bool CApplicationPlayer::GetSubtitleCompensateFPS() const +{ + std::shared_ptr player = GetInternal(); + if (player) + return player->GetSubtitleCompensateFPS(); + return false; +} + void CApplicationPlayer::SetAVDelay(float fValue) { std::shared_ptr player = GetInternal(); @@ -765,7 +780,8 @@ void CApplicationPlayer::GetAudioCapabilities(std::vector& audioCaps) const player->GetAudioCapabilities(audioCaps); } -void CApplicationPlayer::GetSubtitleCapabilities(std::vector& subCaps) const +void CApplicationPlayer::GetSubtitleCapabilities( + std::vector& subCaps) const { const std::shared_ptr player = GetInternal(); if (player) diff --git a/xbmc/application/ApplicationPlayer.h b/xbmc/application/ApplicationPlayer.h index 2a641d0815849..a2cc8ee6e9981 100644 --- a/xbmc/application/ApplicationPlayer.h +++ b/xbmc/application/ApplicationPlayer.h @@ -95,8 +95,9 @@ class CApplicationPlayer : public IApplicationComponent std::string GetPlayerState(); PLAYLIST::Id GetPreferredPlaylist() const; int GetSubtitleDelay() const; + bool GetSubtitleCompensateFPS() const; int GetSubtitle(); - void GetSubtitleCapabilities(std::vector& subCaps) const; + void GetSubtitleCapabilities(std::vector& subCaps) const; int GetSubtitleCount() const; void GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) const; bool GetSubtitleVisible() const; @@ -152,6 +153,7 @@ class CApplicationPlayer : public IApplicationComponent bool SetPlayerState(const std::string& state); void SetSubtitle(int iStream); void SetSubTitleDelay(float fValue = 0.0f); + void SetSubtitleCompensateFPS(bool bDoCompensate = false); void SetSubtitleVisible(bool bVisible); /*! diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h index 9b04674c41573..77fca49088dc6 100644 --- a/xbmc/cores/IPlayer.h +++ b/xbmc/cores/IPlayer.h @@ -64,7 +64,8 @@ enum IPlayerSubtitleCapabilities IPC_SUBS_ALL, IPC_SUBS_SELECT, IPC_SUBS_EXTERNAL, - IPC_SUBS_OFFSET + IPC_SUBS_OFFSET, + IPC_SUBS_STRETCH }; enum ERENDERFEATURE @@ -117,6 +118,8 @@ class IPlayer virtual void SetSubTitleDelay(float fValue = 0.0f) {} virtual float GetSubTitleDelay() { return 0.0f; } + virtual void SetSubtitleCompensateFPS(bool bValue = false) {} + virtual bool GetSubtitleCompensateFPS() const { return false; } virtual int GetSubtitleCount() const { return 0; } virtual int GetSubtitle() { return -1; } virtual void GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) const {} @@ -217,7 +220,7 @@ class IPlayer /*! \brief define the subtitle capabilities of the player */ - virtual void GetSubtitleCapabilities(std::vector& subCaps) const + virtual void GetSubtitleCapabilities(std::vector& subCaps) const { subCaps.assign(1, IPC_SUBS_ALL); } diff --git a/xbmc/cores/VideoPlayer/IVideoPlayer.h b/xbmc/cores/VideoPlayer/IVideoPlayer.h index cf30266692fdb..aed583a08e807 100644 --- a/xbmc/cores/VideoPlayer/IVideoPlayer.h +++ b/xbmc/cores/VideoPlayer/IVideoPlayer.h @@ -88,6 +88,8 @@ class IDVDStreamPlayerVideo : public IDVDStreamPlayer virtual bool IsSubtitleEnabled() = 0; virtual double GetSubtitleDelay() = 0; virtual void SetSubtitleDelay(double delay) = 0; + virtual bool GetSubtitleCompensateFPS() const = 0; + virtual void SetSubtitleCompensateFPS(bool bDoCompensate) = 0; bool IsStalled() const override = 0; virtual bool IsRewindStalled() const { return false; } virtual double GetCurrentPts() = 0; diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp index 22ff11050108d..65672875d9fab 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp @@ -3347,6 +3347,17 @@ float CVideoPlayer::GetSubTitleDelay() return (float) -m_VideoPlayerVideo->GetSubtitleDelay() / DVD_TIME_BASE; } +void CVideoPlayer::SetSubtitleCompensateFPS(bool bDoCompensate) +{ + m_processInfo->GetVideoSettingsLocked().SetSubtitleCompensateFPS(bDoCompensate); + m_VideoPlayerVideo->SetSubtitleCompensateFPS(bDoCompensate); +} + +bool CVideoPlayer::GetSubtitleCompensateFPS() const +{ + return m_VideoPlayerVideo->GetSubtitleCompensateFPS(); +} + bool CVideoPlayer::GetSubtitleVisible() const { if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.h b/xbmc/cores/VideoPlayer/VideoPlayer.h index 01cbca01197f6..deb5a4c44a72a 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayer.h +++ b/xbmc/cores/VideoPlayer/VideoPlayer.h @@ -282,6 +282,8 @@ class CVideoPlayer : public IPlayer, public CThread, public IVideoPlayer, void SetSubTitleDelay(float fValue = 0.0f) override; float GetSubTitleDelay() override; + void SetSubtitleCompensateFPS(bool bValue) override; + bool GetSubtitleCompensateFPS() const override; int GetSubtitleCount() const override; int GetSubtitle() override; void GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) const override; diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp index 092470370244a..2730c7b9d8712 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp @@ -63,6 +63,7 @@ CVideoPlayerVideo::CVideoPlayerVideo(CDVDClock* pClock m_paused = false; m_syncState = IDVDStreamPlayer::SYNC_STARTING; m_iSubtitleDelay = 0; + m_subtitleCompensateFPS = 0.0; m_iLateFrames = 0; m_iDroppedRequest = 0; m_fForcedAspectRatio = 0; @@ -802,9 +803,26 @@ void CVideoPlayerVideo::Flush(bool sync) void CVideoPlayerVideo::ProcessOverlays(const VideoPicture* pSource, double pts) { + double pts1 = pts; + if (m_subtitleCompensateFPS) + { + double stFrameRate = m_fFrameRate; + while (stFrameRate > 44.0) + stFrameRate /= 2.0; + if ((stFrameRate > 24.9 && stFrameRate < 25.1) || (stFrameRate > 49.9 && stFrameRate < 50.1)) + // 25fps material, 24fps subtitles + // use 23.976 since it's waay more common + // in the ultra-rare cases of true 24.0 fps, it's still only a 86 frames + // = ~ 3.6 seconds difference per hour, something I feel one could deal + // with using the subtitle offset feature if need be. + pts1 = pts * (stFrameRate / 23.976); + else // 24 fps material, compensate 25fps subtitles + pts1 = pts * (stFrameRate / 25.0); + } + // remove any overlays that are out of time if (m_syncState == IDVDStreamPlayer::SYNC_INSYNC) - m_pOverlayContainer->CleanUp(pts - m_iSubtitleDelay); + m_pOverlayContainer->CleanUp(pts1 - m_iSubtitleDelay); VecOverlays overlays; @@ -822,7 +840,7 @@ void CVideoPlayerVideo::ProcessOverlays(const VideoPicture* pSource, double pts) if(!pOverlay->bForced && !m_bRenderSubs) continue; - double pts2 = pOverlay->bForced ? pts : pts - m_iSubtitleDelay; + double pts2 = pOverlay->bForced ? pts1 : pts1 - m_iSubtitleDelay; if((pOverlay->iPTSStartTime <= pts2 && (pOverlay->iPTSStopTime > pts2 || pOverlay->iPTSStopTime == 0LL))) { @@ -837,7 +855,7 @@ void CVideoPlayerVideo::ProcessOverlays(const VideoPicture* pSource, double pts) for(it = overlays.begin(); it != overlays.end(); ++it) { - double pts2 = (*it)->bForced ? pts : pts - m_iSubtitleDelay; + double pts2 = (*it)->bForced ? pts1 : pts1 - m_iSubtitleDelay; m_renderManager.AddOverlay(*it, pts2); } } diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.h b/xbmc/cores/VideoPlayer/VideoPlayerVideo.h index d0c83172f0748..734845e8ead4a 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.h +++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.h @@ -65,6 +65,11 @@ class CVideoPlayerVideo : public CThread, public IDVDStreamPlayerVideo bool IsSubtitleEnabled() override { return m_bRenderSubs; } double GetSubtitleDelay() override { return m_iSubtitleDelay; } void SetSubtitleDelay(double delay) override { m_iSubtitleDelay = delay; } + bool GetSubtitleCompensateFPS() const override { return m_subtitleCompensateFPS; } + void SetSubtitleCompensateFPS(bool bDoCompensate) override + { + m_subtitleCompensateFPS = bDoCompensate; + } bool IsStalled() const override { return m_stalled; } bool IsRewindStalled() const override { return m_rewindStalled; } double GetCurrentPts() override; @@ -105,6 +110,7 @@ class CVideoPlayerVideo : public CThread, public IDVDStreamPlayerVideo int CalcDropRequirement(double pts); double m_iSubtitleDelay; + bool m_subtitleCompensateFPS; int m_iLateFrames; int m_iDroppedFrames; diff --git a/xbmc/cores/VideoSettings.cpp b/xbmc/cores/VideoSettings.cpp index ec474b3ff95dd..0d770d5a83615 100644 --- a/xbmc/cores/VideoSettings.cpp +++ b/xbmc/cores/VideoSettings.cpp @@ -24,6 +24,7 @@ CVideoSettings::CVideoSettings() m_AudioStream = -1; m_SubtitleStream = -1; m_SubtitleDelay = 0.0f; + m_subtitleCompensateFPS = false; m_subtitleVerticalPosition = 0; m_subtitleVerticalPositionSave = false; m_SubtitleOn = true; @@ -57,6 +58,8 @@ bool CVideoSettings::operator!=(const CVideoSettings &right) const if (m_AudioStream != right.m_AudioStream) return true; if (m_SubtitleStream != right.m_SubtitleStream) return true; if (m_SubtitleDelay != right.m_SubtitleDelay) return true; + if (m_subtitleCompensateFPS != right.m_subtitleCompensateFPS) + return true; if (m_subtitleVerticalPosition != right.m_subtitleVerticalPosition) return true; if (m_subtitleVerticalPositionSave != right.m_subtitleVerticalPositionSave) @@ -125,6 +128,12 @@ void CVideoSettingsLocked::SetSubtitleDelay(float delay) m_videoSettings.m_SubtitleDelay = delay; } +void CVideoSettingsLocked::SetSubtitleCompensateFPS(bool doCompensate) +{ + std::unique_lock lock(m_critSection); + m_videoSettings.m_subtitleCompensateFPS = doCompensate; +} + void CVideoSettingsLocked::SetSubtitleVerticalPosition(int value, bool save) { std::unique_lock lock(m_critSection); diff --git a/xbmc/cores/VideoSettings.h b/xbmc/cores/VideoSettings.h index a7135da5d8734..a20637c8a036c 100644 --- a/xbmc/cores/VideoSettings.h +++ b/xbmc/cores/VideoSettings.h @@ -210,6 +210,7 @@ class CVideoSettings float m_VolumeAmplification; int m_SubtitleStream; float m_SubtitleDelay; + double m_subtitleCompensateFPS; int m_subtitleVerticalPosition{0}; bool m_subtitleVerticalPositionSave{false}; bool m_SubtitleOn; @@ -246,6 +247,7 @@ class CVideoSettingsLocked void SetVideoStream(int stream); void SetAudioDelay(float delay); void SetSubtitleDelay(float delay); + void SetSubtitleCompensateFPS(bool doCompensate); /*! * \brief Set the subtitle vertical position, diff --git a/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp b/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp index 171c69a294bfe..7e8ed9485cbb5 100644 --- a/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp +++ b/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp @@ -46,6 +46,7 @@ #define SETTING_SUBTITLE_ENABLE "subtitles.enable" #define SETTING_SUBTITLE_DELAY "subtitles.delay" +#define SETTING_SUBTITLE_FPS "subtitles.fps" #define SETTING_SUBTITLE_STREAM "subtitles.stream" #define SETTING_SUBTITLE_BROWSER "subtitles.browser" #define SETTING_SUBTITLE_SEARCH "subtitles.search" @@ -111,6 +112,11 @@ void CGUIDialogSubtitleSettings::OnSettingChanged(const std::shared_ptr(std::static_pointer_cast(setting)->GetValue()); appPlayer->SetSubTitleDelay(value); } + else if (settingId == SETTING_SUBTITLE_FPS) + { + bool value = std::static_pointer_cast(setting)->GetValue(); + appPlayer->SetSubtitleCompensateFPS(value); + } else if (settingId == SETTING_SUBTITLE_STREAM) { m_subtitleStream = std::static_pointer_cast(setting)->GetValue(); @@ -303,6 +309,13 @@ void CGUIDialogSubtitleSettings::InitializeSettings() std::static_pointer_cast(settingSubtitleDelay->GetControl())->SetFormatter(SettingFormatterDelay); } + if (SupportsSubtitleFeature(IPC_SUBS_STRETCH)) + { + m_subtitleCompensateFPS = appPlayer->GetSubtitleCompensateFPS(); + AddToggle(groupSubtitles, SETTING_SUBTITLE_FPS, 39202, SettingLevel::Basic, + m_subtitleCompensateFPS); + } + // subtitle stream setting if (SupportsSubtitleFeature(IPC_SUBS_SELECT)) AddSubtitleStreams(groupSubtitles, SETTING_SUBTITLE_STREAM); diff --git a/xbmc/video/dialogs/GUIDialogSubtitleSettings.h b/xbmc/video/dialogs/GUIDialogSubtitleSettings.h index 65216ede389c9..c9cd963886d0d 100644 --- a/xbmc/video/dialogs/GUIDialogSubtitleSettings.h +++ b/xbmc/video/dialogs/GUIDialogSubtitleSettings.h @@ -8,6 +8,7 @@ #pragma once +#include "cores/IPlayer.h" #include "cores/VideoPlayer/Interface/StreamInfo.h" #include "settings/dialogs/GUIDialogSettingsManualBase.h" @@ -51,9 +52,10 @@ class CGUIDialogSubtitleSettings : public CGUIDialogSettingsManualBase int m_subtitleStream; bool m_subtitleVisible; + bool m_subtitleCompensateFPS; std::shared_ptr m_subtitleStreamSetting; - std::vector m_subtitleCapabilities; + std::vector m_subtitleCapabilities; static std::string FormatFlags(StreamFlags flags); static void SubtitleStreamsOptionFiller(const std::shared_ptr& setting,