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 "music/MusicFileItemClassify.h"
23 #include "settings/AdvancedSettings.h"
24 #include "settings/Settings.h"
25 #include "settings/SettingsComponent.h"
26 #include "settings/lib/Setting.h"
27 #include "settings/lib/SettingDefinitions.h"
28 #include "utils/MathUtils.h"
29 #include "utils/StringUtils.h"
30 #include "utils/Variant.h"
31 #include "utils/log.h"
32 #include "windowing/GraphicContext.h"
40 CSeekHandler::~CSeekHandler()
43 m_forwardSeekSteps
.clear();
44 m_backwardSeekSteps
.clear();
47 void CSeekHandler::Configure()
51 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
54 m_seekDelays
.insert(std::make_pair(SEEK_TYPE_VIDEO
, settings
->GetInt(CSettings::SETTING_VIDEOPLAYER_SEEKDELAY
)));
55 m_seekDelays
.insert(std::make_pair(SEEK_TYPE_MUSIC
, settings
->GetInt(CSettings::SETTING_MUSICPLAYER_SEEKDELAY
)));
57 m_forwardSeekSteps
.clear();
58 m_backwardSeekSteps
.clear();
60 std::map
<SeekType
, std::string
> seekTypeSettingMap
;
61 seekTypeSettingMap
.insert(std::make_pair(SEEK_TYPE_VIDEO
, CSettings::SETTING_VIDEOPLAYER_SEEKSTEPS
));
62 seekTypeSettingMap
.insert(std::make_pair(SEEK_TYPE_MUSIC
, CSettings::SETTING_MUSICPLAYER_SEEKSTEPS
));
64 for (std::map
<SeekType
, std::string
>::iterator it
= seekTypeSettingMap
.begin(); it
!=seekTypeSettingMap
.end(); ++it
)
66 std::vector
<int> forwardSeekSteps
;
67 std::vector
<int> backwardSeekSteps
;
69 std::vector
<CVariant
> seekSteps
= settings
->GetList(it
->second
);
70 for (std::vector
<CVariant
>::iterator it
= seekSteps
.begin(); it
!= seekSteps
.end(); ++it
)
72 int stepSeconds
= static_cast<int>((*it
).asInteger());
74 backwardSeekSteps
.insert(backwardSeekSteps
.begin(), stepSeconds
);
76 forwardSeekSteps
.push_back(stepSeconds
);
79 m_forwardSeekSteps
.insert(std::make_pair(it
->first
, forwardSeekSteps
));
80 m_backwardSeekSteps
.insert(std::make_pair(it
->first
, backwardSeekSteps
));
84 void CSeekHandler::Reset()
86 m_requireSeek
= false;
90 m_timeCodePosition
= 0;
93 int CSeekHandler::GetSeekStepSize(SeekType type
, int step
)
98 std::vector
<int> seekSteps(step
> 0 ? m_forwardSeekSteps
.at(type
) : m_backwardSeekSteps
.at(type
));
100 if (seekSteps
.empty())
102 CLog::Log(LOGERROR
, "SeekHandler - {} - No {} {} seek steps configured.", __FUNCTION__
,
103 (type
== SeekType::SEEK_TYPE_VIDEO
? "video" : "music"),
104 (step
> 0 ? "forward" : "backward"));
110 // when exceeding the selected amount of steps repeat/sum up the last step size
111 if (static_cast<size_t>(abs(step
)) <= seekSteps
.size())
112 seconds
= seekSteps
.at(abs(step
) - 1);
114 seconds
= seekSteps
.back() * (abs(step
) - seekSteps
.size() + 1);
119 void CSeekHandler::Seek(bool forward
, float amount
, float duration
/* = 0 */, bool analogSeek
/* = false */, SeekType type
/* = SEEK_TYPE_VIDEO */)
121 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
126 // use only the first step forward/backward for a seek without a delay
127 if (!analogSeek
&& m_seekDelays
.at(type
) == 0)
129 SeekSeconds(GetSeekStepSize(type
, forward
? 1 : -1));
133 m_requireSeek
= true;
134 m_analogSeek
= analogSeek
;
135 m_seekDelay
= analogSeek
? analogSeekDelay
: m_seekDelays
.at(type
);
138 // calculate our seek amount
141 //100% over 1 second.
142 float speed
= 100.0f
;
146 speed
/= CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS();
148 double totalTime
= g_application
.GetTotalTime();
152 double seekSize
= static_cast<double>(amount
* amount
* speed
) * totalTime
/ 100.0;
154 SetSeekSize(m_seekSize
+ seekSize
);
156 SetSeekSize(m_seekSize
- seekSize
);
160 m_seekStep
+= forward
? 1 : -1;
161 int seekSeconds
= GetSeekStepSize(type
, m_seekStep
);
162 if (seekSeconds
!= 0)
164 SetSeekSize(seekSeconds
);
168 // nothing to do, abort seeking
172 m_seekChanged
= true;
176 void CSeekHandler::SeekSeconds(int seconds
)
178 // abort if we do not have a play time or already perform a seek
182 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
183 SetSeekSize(seconds
);
185 // perform relative seek
186 auto& components
= CServiceBroker::GetAppComponents();
187 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
188 appPlayer
->SeekTimeRelative(static_cast<int64_t>(seconds
* 1000));
193 int CSeekHandler::GetSeekSize() const
195 return MathUtils::round_int(m_seekSize
);
198 void CSeekHandler::SetSeekSize(double seekSize
)
200 const auto& components
= CServiceBroker::GetAppComponents();
201 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
202 int64_t playTime
= appPlayer
->GetTime();
203 double minSeekSize
= (appPlayer
->GetMinTime() - playTime
) / 1000.0;
204 double maxSeekSize
= (appPlayer
->GetMaxTime() - playTime
) / 1000.0;
206 m_seekSize
= seekSize
> 0
207 ? std::min(seekSize
, maxSeekSize
)
208 : std::max(seekSize
, minSeekSize
);
211 bool CSeekHandler::InProgress() const
213 return m_requireSeek
|| CServiceBroker::GetDataCacheCore().IsSeeking();
216 void CSeekHandler::FrameMove()
218 if (m_timer
.GetElapsedMilliseconds() >= m_seekDelay
&& m_requireSeek
)
220 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
222 // perform relative seek
223 auto& components
= CServiceBroker::GetAppComponents();
224 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
225 appPlayer
->SeekTimeRelative(static_cast<int64_t>(m_seekSize
* 1000));
227 m_seekChanged
= true;
232 if (m_timeCodePosition
> 0 && m_timerTimeCode
.GetElapsedMilliseconds() >= 2500)
234 m_timeCodePosition
= 0;
239 m_seekChanged
= false;
240 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_STATE_CHANGED
);
244 void CSeekHandler::SettingOptionsSeekStepsFiller(const SettingConstPtr
& setting
,
245 std::vector
<IntegerSettingOption
>& list
,
250 for (int seconds
: CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_seekSteps
)
253 label
= StringUtils::Format(g_localizeStrings
.Get(14044), seconds
/ 60);
255 label
= StringUtils::Format(g_localizeStrings
.Get(14045), seconds
);
257 list
.insert(list
.begin(), IntegerSettingOption("-" + label
, seconds
* -1));
258 list
.emplace_back(label
, seconds
);
262 void CSeekHandler::OnSettingChanged(const std::shared_ptr
<const CSetting
>& setting
)
267 if (setting
->GetId() == CSettings::SETTING_VIDEOPLAYER_SEEKDELAY
||
268 setting
->GetId() == CSettings::SETTING_VIDEOPLAYER_SEEKSTEPS
||
269 setting
->GetId() == CSettings::SETTING_MUSICPLAYER_SEEKDELAY
||
270 setting
->GetId() == CSettings::SETTING_MUSICPLAYER_SEEKSTEPS
)
274 bool CSeekHandler::OnAction(const CAction
&action
)
276 auto& components
= CServiceBroker::GetAppComponents();
277 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
278 if (!appPlayer
->IsPlaying() || !appPlayer
->CanSeek())
282 MUSIC::IsAudio(g_application
.CurrentFileItem()) ? SEEK_TYPE_MUSIC
: SEEK_TYPE_VIDEO
;
284 if (SeekTimeCode(action
))
287 switch (action
.GetID())
289 case ACTION_SMALL_STEP_BACK
:
290 case ACTION_STEP_BACK
:
292 Seek(false, action
.GetAmount(), action
.GetRepeat(), false, type
);
295 case ACTION_STEP_FORWARD
:
297 Seek(true, action
.GetAmount(), action
.GetRepeat(), false, type
);
300 case ACTION_BIG_STEP_BACK
:
301 case ACTION_CHAPTER_OR_BIG_STEP_BACK
:
303 appPlayer
->Seek(false, true, action
.GetID() == ACTION_CHAPTER_OR_BIG_STEP_BACK
);
306 case ACTION_BIG_STEP_FORWARD
:
307 case ACTION_CHAPTER_OR_BIG_STEP_FORWARD
:
309 appPlayer
->Seek(true, true, action
.GetID() == ACTION_CHAPTER_OR_BIG_STEP_FORWARD
);
312 case ACTION_NEXT_SCENE
:
314 appPlayer
->SeekScene(Direction::FORWARD
);
317 case ACTION_PREV_SCENE
:
319 appPlayer
->SeekScene(Direction::BACKWARD
);
322 case ACTION_ANALOG_SEEK_FORWARD
:
323 case ACTION_ANALOG_SEEK_BACK
:
325 if (action
.GetAmount())
326 Seek(action
.GetID() == ACTION_ANALOG_SEEK_FORWARD
, action
.GetAmount(), action
.GetRepeat(), true);
339 case ACTION_JUMP_SMS2
:
340 case ACTION_JUMP_SMS3
:
341 case ACTION_JUMP_SMS4
:
342 case ACTION_JUMP_SMS5
:
343 case ACTION_JUMP_SMS6
:
344 case ACTION_JUMP_SMS7
:
345 case ACTION_JUMP_SMS8
:
346 case ACTION_JUMP_SMS9
:
348 if (!g_application
.CurrentFileItem().IsLiveTV())
350 ChangeTimeCode(action
.GetID());
362 bool CSeekHandler::SeekTimeCode(const CAction
&action
)
364 if (m_timeCodePosition
<= 0)
367 switch (action
.GetID())
369 case ACTION_SELECT_ITEM
:
370 case ACTION_PLAYER_PLAY
:
373 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
375 g_application
.SeekTime(GetTimeCodeSeconds());
379 case ACTION_SMALL_STEP_BACK
:
380 case ACTION_STEP_BACK
:
381 case ACTION_BIG_STEP_BACK
:
382 case ACTION_CHAPTER_OR_BIG_STEP_BACK
:
383 case ACTION_MOVE_LEFT
:
385 SeekSeconds(-GetTimeCodeSeconds());
388 case ACTION_STEP_FORWARD
:
389 case ACTION_BIG_STEP_FORWARD
:
390 case ACTION_CHAPTER_OR_BIG_STEP_FORWARD
:
391 case ACTION_MOVE_RIGHT
:
393 SeekSeconds(GetTimeCodeSeconds());
402 void CSeekHandler::ChangeTimeCode(int remote
)
404 if (remote
>= ACTION_JUMP_SMS2
&& remote
<= ACTION_JUMP_SMS9
)
407 remote
-= (ACTION_JUMP_SMS2
- REMOTE_2
);
409 if (remote
>= REMOTE_0
&& remote
<= REMOTE_9
)
411 m_timerTimeCode
.StartZero();
413 if (m_timeCodePosition
< 6)
414 m_timeCodeStamp
[m_timeCodePosition
++] = remote
- REMOTE_0
;
418 for (int i
= 0; i
< 5; i
++)
419 m_timeCodeStamp
[i
] = m_timeCodeStamp
[i
+ 1];
420 m_timeCodeStamp
[5] = remote
- REMOTE_0
;
425 int CSeekHandler::GetTimeCodeSeconds() const
427 if (m_timeCodePosition
> 0)
429 // Convert the timestamp into an integer
431 for (int i
= 0; i
< m_timeCodePosition
; i
++)
432 tot
= tot
* 10 + m_timeCodeStamp
[i
];
434 // Interpret result as HHMMSS
435 int s
= tot
% 100; tot
/= 100;
436 int m
= tot
% 100; tot
/= 100;
439 return h
* 3600 + m
* 60 + s
;