2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "SeekHandler.h"
12 #include "ServiceBroker.h"
13 #include "application/Application.h"
14 #include "application/ApplicationComponents.h"
15 #include "application/ApplicationPlayer.h"
16 #include "cores/DataCacheCore.h"
17 #include "guilib/GUIComponent.h"
18 #include "guilib/GUIWindowManager.h"
19 #include "guilib/LocalizeStrings.h"
20 #include "input/actions/Action.h"
21 #include "input/actions/ActionIDs.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "settings/lib/Setting.h"
26 #include "settings/lib/SettingDefinitions.h"
27 #include "utils/MathUtils.h"
28 #include "utils/StringUtils.h"
29 #include "utils/Variant.h"
30 #include "utils/log.h"
31 #include "windowing/GraphicContext.h"
37 CSeekHandler::~CSeekHandler()
40 m_forwardSeekSteps
.clear();
41 m_backwardSeekSteps
.clear();
44 void CSeekHandler::Configure()
48 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
51 m_seekDelays
.insert(std::make_pair(SEEK_TYPE_VIDEO
, settings
->GetInt(CSettings::SETTING_VIDEOPLAYER_SEEKDELAY
)));
52 m_seekDelays
.insert(std::make_pair(SEEK_TYPE_MUSIC
, settings
->GetInt(CSettings::SETTING_MUSICPLAYER_SEEKDELAY
)));
54 m_forwardSeekSteps
.clear();
55 m_backwardSeekSteps
.clear();
57 std::map
<SeekType
, std::string
> seekTypeSettingMap
;
58 seekTypeSettingMap
.insert(std::make_pair(SEEK_TYPE_VIDEO
, CSettings::SETTING_VIDEOPLAYER_SEEKSTEPS
));
59 seekTypeSettingMap
.insert(std::make_pair(SEEK_TYPE_MUSIC
, CSettings::SETTING_MUSICPLAYER_SEEKSTEPS
));
61 for (std::map
<SeekType
, std::string
>::iterator it
= seekTypeSettingMap
.begin(); it
!=seekTypeSettingMap
.end(); ++it
)
63 std::vector
<int> forwardSeekSteps
;
64 std::vector
<int> backwardSeekSteps
;
66 std::vector
<CVariant
> seekSteps
= settings
->GetList(it
->second
);
67 for (std::vector
<CVariant
>::iterator it
= seekSteps
.begin(); it
!= seekSteps
.end(); ++it
)
69 int stepSeconds
= static_cast<int>((*it
).asInteger());
71 backwardSeekSteps
.insert(backwardSeekSteps
.begin(), stepSeconds
);
73 forwardSeekSteps
.push_back(stepSeconds
);
76 m_forwardSeekSteps
.insert(std::make_pair(it
->first
, forwardSeekSteps
));
77 m_backwardSeekSteps
.insert(std::make_pair(it
->first
, backwardSeekSteps
));
81 void CSeekHandler::Reset()
83 m_requireSeek
= false;
87 m_timeCodePosition
= 0;
90 int CSeekHandler::GetSeekStepSize(SeekType type
, int step
)
95 std::vector
<int> seekSteps(step
> 0 ? m_forwardSeekSteps
.at(type
) : m_backwardSeekSteps
.at(type
));
97 if (seekSteps
.empty())
99 CLog::Log(LOGERROR
, "SeekHandler - {} - No {} {} seek steps configured.", __FUNCTION__
,
100 (type
== SeekType::SEEK_TYPE_VIDEO
? "video" : "music"),
101 (step
> 0 ? "forward" : "backward"));
107 // when exceeding the selected amount of steps repeat/sum up the last step size
108 if (static_cast<size_t>(abs(step
)) <= seekSteps
.size())
109 seconds
= seekSteps
.at(abs(step
) - 1);
111 seconds
= seekSteps
.back() * (abs(step
) - seekSteps
.size() + 1);
116 void CSeekHandler::Seek(bool forward
, float amount
, float duration
/* = 0 */, bool analogSeek
/* = false */, SeekType type
/* = SEEK_TYPE_VIDEO */)
118 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
123 // use only the first step forward/backward for a seek without a delay
124 if (!analogSeek
&& m_seekDelays
.at(type
) == 0)
126 SeekSeconds(GetSeekStepSize(type
, forward
? 1 : -1));
130 m_requireSeek
= true;
131 m_analogSeek
= analogSeek
;
132 m_seekDelay
= analogSeek
? analogSeekDelay
: m_seekDelays
.at(type
);
135 // calculate our seek amount
138 //100% over 1 second.
139 float speed
= 100.0f
;
143 speed
/= CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS();
145 double totalTime
= g_application
.GetTotalTime();
149 double seekSize
= static_cast<double>(amount
* amount
* speed
) * totalTime
/ 100.0;
151 SetSeekSize(m_seekSize
+ seekSize
);
153 SetSeekSize(m_seekSize
- seekSize
);
157 m_seekStep
+= forward
? 1 : -1;
158 int seekSeconds
= GetSeekStepSize(type
, m_seekStep
);
159 if (seekSeconds
!= 0)
161 SetSeekSize(seekSeconds
);
165 // nothing to do, abort seeking
169 m_seekChanged
= true;
173 void CSeekHandler::SeekSeconds(int seconds
)
175 // abort if we do not have a play time or already perform a seek
179 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
180 SetSeekSize(seconds
);
182 // perform relative seek
183 auto& components
= CServiceBroker::GetAppComponents();
184 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
185 appPlayer
->SeekTimeRelative(static_cast<int64_t>(seconds
* 1000));
190 int CSeekHandler::GetSeekSize() const
192 return MathUtils::round_int(m_seekSize
);
195 void CSeekHandler::SetSeekSize(double seekSize
)
197 const auto& components
= CServiceBroker::GetAppComponents();
198 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
199 int64_t playTime
= appPlayer
->GetTime();
200 double minSeekSize
= (appPlayer
->GetMinTime() - playTime
) / 1000.0;
201 double maxSeekSize
= (appPlayer
->GetMaxTime() - playTime
) / 1000.0;
203 m_seekSize
= seekSize
> 0
204 ? std::min(seekSize
, maxSeekSize
)
205 : std::max(seekSize
, minSeekSize
);
208 bool CSeekHandler::InProgress() const
210 return m_requireSeek
|| CServiceBroker::GetDataCacheCore().IsSeeking();
213 void CSeekHandler::FrameMove()
215 if (m_timer
.GetElapsedMilliseconds() >= m_seekDelay
&& m_requireSeek
)
217 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
219 // perform relative seek
220 auto& components
= CServiceBroker::GetAppComponents();
221 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
222 appPlayer
->SeekTimeRelative(static_cast<int64_t>(m_seekSize
* 1000));
224 m_seekChanged
= true;
229 if (m_timeCodePosition
> 0 && m_timerTimeCode
.GetElapsedMilliseconds() >= 2500)
231 m_timeCodePosition
= 0;
236 m_seekChanged
= false;
237 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_STATE_CHANGED
);
241 void CSeekHandler::SettingOptionsSeekStepsFiller(const SettingConstPtr
& setting
,
242 std::vector
<IntegerSettingOption
>& list
,
247 for (int seconds
: CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_seekSteps
)
250 label
= StringUtils::Format(g_localizeStrings
.Get(14044), seconds
/ 60);
252 label
= StringUtils::Format(g_localizeStrings
.Get(14045), seconds
);
254 list
.insert(list
.begin(), IntegerSettingOption("-" + label
, seconds
* -1));
255 list
.emplace_back(label
, seconds
);
259 void CSeekHandler::OnSettingChanged(const std::shared_ptr
<const CSetting
>& setting
)
264 if (setting
->GetId() == CSettings::SETTING_VIDEOPLAYER_SEEKDELAY
||
265 setting
->GetId() == CSettings::SETTING_VIDEOPLAYER_SEEKSTEPS
||
266 setting
->GetId() == CSettings::SETTING_MUSICPLAYER_SEEKDELAY
||
267 setting
->GetId() == CSettings::SETTING_MUSICPLAYER_SEEKSTEPS
)
271 bool CSeekHandler::OnAction(const CAction
&action
)
273 auto& components
= CServiceBroker::GetAppComponents();
274 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
275 if (!appPlayer
->IsPlaying() || !appPlayer
->CanSeek())
278 SeekType type
= g_application
.CurrentFileItem().IsAudio() ? SEEK_TYPE_MUSIC
: SEEK_TYPE_VIDEO
;
280 if (SeekTimeCode(action
))
283 switch (action
.GetID())
285 case ACTION_SMALL_STEP_BACK
:
286 case ACTION_STEP_BACK
:
288 Seek(false, action
.GetAmount(), action
.GetRepeat(), false, type
);
291 case ACTION_STEP_FORWARD
:
293 Seek(true, action
.GetAmount(), action
.GetRepeat(), false, type
);
296 case ACTION_BIG_STEP_BACK
:
297 case ACTION_CHAPTER_OR_BIG_STEP_BACK
:
299 appPlayer
->Seek(false, true, action
.GetID() == ACTION_CHAPTER_OR_BIG_STEP_BACK
);
302 case ACTION_BIG_STEP_FORWARD
:
303 case ACTION_CHAPTER_OR_BIG_STEP_FORWARD
:
305 appPlayer
->Seek(true, true, action
.GetID() == ACTION_CHAPTER_OR_BIG_STEP_FORWARD
);
308 case ACTION_NEXT_SCENE
:
310 appPlayer
->SeekScene(true);
313 case ACTION_PREV_SCENE
:
315 appPlayer
->SeekScene(false);
318 case ACTION_ANALOG_SEEK_FORWARD
:
319 case ACTION_ANALOG_SEEK_BACK
:
321 if (action
.GetAmount())
322 Seek(action
.GetID() == ACTION_ANALOG_SEEK_FORWARD
, action
.GetAmount(), action
.GetRepeat(), true);
335 case ACTION_JUMP_SMS2
:
336 case ACTION_JUMP_SMS3
:
337 case ACTION_JUMP_SMS4
:
338 case ACTION_JUMP_SMS5
:
339 case ACTION_JUMP_SMS6
:
340 case ACTION_JUMP_SMS7
:
341 case ACTION_JUMP_SMS8
:
342 case ACTION_JUMP_SMS9
:
344 if (!g_application
.CurrentFileItem().IsLiveTV())
346 ChangeTimeCode(action
.GetID());
358 bool CSeekHandler::SeekTimeCode(const CAction
&action
)
360 if (m_timeCodePosition
<= 0)
363 switch (action
.GetID())
365 case ACTION_SELECT_ITEM
:
366 case ACTION_PLAYER_PLAY
:
369 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
371 g_application
.SeekTime(GetTimeCodeSeconds());
375 case ACTION_SMALL_STEP_BACK
:
376 case ACTION_STEP_BACK
:
377 case ACTION_BIG_STEP_BACK
:
378 case ACTION_CHAPTER_OR_BIG_STEP_BACK
:
379 case ACTION_MOVE_LEFT
:
381 SeekSeconds(-GetTimeCodeSeconds());
384 case ACTION_STEP_FORWARD
:
385 case ACTION_BIG_STEP_FORWARD
:
386 case ACTION_CHAPTER_OR_BIG_STEP_FORWARD
:
387 case ACTION_MOVE_RIGHT
:
389 SeekSeconds(GetTimeCodeSeconds());
398 void CSeekHandler::ChangeTimeCode(int remote
)
400 if (remote
>= ACTION_JUMP_SMS2
&& remote
<= ACTION_JUMP_SMS9
)
403 remote
-= (ACTION_JUMP_SMS2
- REMOTE_2
);
405 if (remote
>= REMOTE_0
&& remote
<= REMOTE_9
)
407 m_timerTimeCode
.StartZero();
409 if (m_timeCodePosition
< 6)
410 m_timeCodeStamp
[m_timeCodePosition
++] = remote
- REMOTE_0
;
414 for (int i
= 0; i
< 5; i
++)
415 m_timeCodeStamp
[i
] = m_timeCodeStamp
[i
+ 1];
416 m_timeCodeStamp
[5] = remote
- REMOTE_0
;
421 int CSeekHandler::GetTimeCodeSeconds() const
423 if (m_timeCodePosition
> 0)
425 // Convert the timestamp into an integer
427 for (int i
= 0; i
< m_timeCodePosition
; i
++)
428 tot
= tot
* 10 + m_timeCodeStamp
[i
];
430 // Interpret result as HHMMSS
431 int s
= tot
% 100; tot
/= 100;
432 int m
= tot
% 100; tot
/= 100;
435 return h
* 3600 + m
* 60 + s
;