[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / SeekHandler.cpp
blob7d8492634840d4cbdf9fc04ec451932ada407f53
1 /*
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.
7 */
9 #include "SeekHandler.h"
11 #include "FileItem.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"
34 #include <cmath>
35 #include <mutex>
36 #include <stdlib.h>
38 using namespace KODI;
40 CSeekHandler::~CSeekHandler()
42 m_seekDelays.clear();
43 m_forwardSeekSteps.clear();
44 m_backwardSeekSteps.clear();
47 void CSeekHandler::Configure()
49 Reset();
51 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
53 m_seekDelays.clear();
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());
73 if (stepSeconds < 0)
74 backwardSeekSteps.insert(backwardSeekSteps.begin(), stepSeconds);
75 else
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;
87 m_analogSeek = false;
88 m_seekStep = 0;
89 m_seekSize = 0;
90 m_timeCodePosition = 0;
93 int CSeekHandler::GetSeekStepSize(SeekType type, int step)
95 if (step == 0)
96 return 0;
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"));
105 return 0;
108 int seconds = 0;
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);
113 else
114 seconds = seekSteps.back() * (abs(step) - seekSteps.size() + 1);
116 return seconds;
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);
123 // not yet seeking
124 if (!m_requireSeek)
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));
130 return;
133 m_requireSeek = true;
134 m_analogSeek = analogSeek;
135 m_seekDelay = analogSeek ? analogSeekDelay : m_seekDelays.at(type);
138 // calculate our seek amount
139 if (analogSeek)
141 //100% over 1 second.
142 float speed = 100.0f;
143 if( duration )
144 speed *= duration;
145 else
146 speed /= CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS();
148 double totalTime = g_application.GetTotalTime();
149 if (totalTime < 0)
150 totalTime = 0;
152 double seekSize = static_cast<double>(amount * amount * speed) * totalTime / 100.0;
153 if (forward)
154 SetSeekSize(m_seekSize + seekSize);
155 else
156 SetSeekSize(m_seekSize - seekSize);
158 else
160 m_seekStep += forward ? 1 : -1;
161 int seekSeconds = GetSeekStepSize(type, m_seekStep);
162 if (seekSeconds != 0)
164 SetSeekSize(seekSeconds);
166 else
168 // nothing to do, abort seeking
169 Reset();
172 m_seekChanged = true;
173 m_timer.StartZero();
176 void CSeekHandler::SeekSeconds(int seconds)
178 // abort if we do not have a play time or already perform a seek
179 if (seconds == 0)
180 return;
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));
190 Reset();
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;
229 Reset();
232 if (m_timeCodePosition > 0 && m_timerTimeCode.GetElapsedMilliseconds() >= 2500)
234 m_timeCodePosition = 0;
237 if (m_seekChanged)
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,
246 int& current,
247 void* data)
249 std::string label;
250 for (int seconds : CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_seekSteps)
252 if (seconds > 60)
253 label = StringUtils::Format(g_localizeStrings.Get(14044), seconds / 60);
254 else
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)
264 if (setting == NULL)
265 return;
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)
271 Configure();
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())
279 return false;
281 SeekType type =
282 MUSIC::IsAudio(g_application.CurrentFileItem()) ? SEEK_TYPE_MUSIC : SEEK_TYPE_VIDEO;
284 if (SeekTimeCode(action))
285 return true;
287 switch (action.GetID())
289 case ACTION_SMALL_STEP_BACK:
290 case ACTION_STEP_BACK:
292 Seek(false, action.GetAmount(), action.GetRepeat(), false, type);
293 return true;
295 case ACTION_STEP_FORWARD:
297 Seek(true, action.GetAmount(), action.GetRepeat(), false, type);
298 return true;
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);
304 return true;
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);
310 return true;
312 case ACTION_NEXT_SCENE:
314 appPlayer->SeekScene(Direction::FORWARD);
315 return true;
317 case ACTION_PREV_SCENE:
319 appPlayer->SeekScene(Direction::BACKWARD);
320 return true;
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);
327 return true;
329 case REMOTE_0:
330 case REMOTE_1:
331 case REMOTE_2:
332 case REMOTE_3:
333 case REMOTE_4:
334 case REMOTE_5:
335 case REMOTE_6:
336 case REMOTE_7:
337 case REMOTE_8:
338 case REMOTE_9:
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());
351 return true;
354 break;
355 default:
356 break;
359 return false;
362 bool CSeekHandler::SeekTimeCode(const CAction &action)
364 if (m_timeCodePosition <= 0)
365 return false;
367 switch (action.GetID())
369 case ACTION_SELECT_ITEM:
370 case ACTION_PLAYER_PLAY:
371 case ACTION_PAUSE:
373 std::unique_lock<CCriticalSection> lock(m_critSection);
375 g_application.SeekTime(GetTimeCodeSeconds());
376 Reset();
377 return true;
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());
386 return true;
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());
394 return true;
396 default:
397 break;
399 return false;
402 void CSeekHandler::ChangeTimeCode(int remote)
404 if (remote >= ACTION_JUMP_SMS2 && remote <= ACTION_JUMP_SMS9)
406 // cast to REMOTE_X
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;
415 else
417 // rotate around
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
430 int tot = 0;
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;
437 int h = tot % 100;
439 return h * 3600 + m * 60 + s;
441 return 0;