[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / SeekHandler.cpp
blobd686e2832638ae879f229af365c7eea607892112
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 "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"
33 #include <cmath>
34 #include <mutex>
35 #include <stdlib.h>
37 CSeekHandler::~CSeekHandler()
39 m_seekDelays.clear();
40 m_forwardSeekSteps.clear();
41 m_backwardSeekSteps.clear();
44 void CSeekHandler::Configure()
46 Reset();
48 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
50 m_seekDelays.clear();
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());
70 if (stepSeconds < 0)
71 backwardSeekSteps.insert(backwardSeekSteps.begin(), stepSeconds);
72 else
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;
84 m_analogSeek = false;
85 m_seekStep = 0;
86 m_seekSize = 0;
87 m_timeCodePosition = 0;
90 int CSeekHandler::GetSeekStepSize(SeekType type, int step)
92 if (step == 0)
93 return 0;
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"));
102 return 0;
105 int seconds = 0;
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);
110 else
111 seconds = seekSteps.back() * (abs(step) - seekSteps.size() + 1);
113 return seconds;
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);
120 // not yet seeking
121 if (!m_requireSeek)
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));
127 return;
130 m_requireSeek = true;
131 m_analogSeek = analogSeek;
132 m_seekDelay = analogSeek ? analogSeekDelay : m_seekDelays.at(type);
135 // calculate our seek amount
136 if (analogSeek)
138 //100% over 1 second.
139 float speed = 100.0f;
140 if( duration )
141 speed *= duration;
142 else
143 speed /= CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS();
145 double totalTime = g_application.GetTotalTime();
146 if (totalTime < 0)
147 totalTime = 0;
149 double seekSize = static_cast<double>(amount * amount * speed) * totalTime / 100.0;
150 if (forward)
151 SetSeekSize(m_seekSize + seekSize);
152 else
153 SetSeekSize(m_seekSize - seekSize);
155 else
157 m_seekStep += forward ? 1 : -1;
158 int seekSeconds = GetSeekStepSize(type, m_seekStep);
159 if (seekSeconds != 0)
161 SetSeekSize(seekSeconds);
163 else
165 // nothing to do, abort seeking
166 Reset();
169 m_seekChanged = true;
170 m_timer.StartZero();
173 void CSeekHandler::SeekSeconds(int seconds)
175 // abort if we do not have a play time or already perform a seek
176 if (seconds == 0)
177 return;
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));
187 Reset();
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;
226 Reset();
229 if (m_timeCodePosition > 0 && m_timerTimeCode.GetElapsedMilliseconds() >= 2500)
231 m_timeCodePosition = 0;
234 if (m_seekChanged)
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,
243 int& current,
244 void* data)
246 std::string label;
247 for (int seconds : CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_seekSteps)
249 if (seconds > 60)
250 label = StringUtils::Format(g_localizeStrings.Get(14044), seconds / 60);
251 else
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)
261 if (setting == NULL)
262 return;
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)
268 Configure();
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())
276 return false;
278 SeekType type = g_application.CurrentFileItem().IsAudio() ? SEEK_TYPE_MUSIC : SEEK_TYPE_VIDEO;
280 if (SeekTimeCode(action))
281 return true;
283 switch (action.GetID())
285 case ACTION_SMALL_STEP_BACK:
286 case ACTION_STEP_BACK:
288 Seek(false, action.GetAmount(), action.GetRepeat(), false, type);
289 return true;
291 case ACTION_STEP_FORWARD:
293 Seek(true, action.GetAmount(), action.GetRepeat(), false, type);
294 return true;
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);
300 return true;
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);
306 return true;
308 case ACTION_NEXT_SCENE:
310 appPlayer->SeekScene(true);
311 return true;
313 case ACTION_PREV_SCENE:
315 appPlayer->SeekScene(false);
316 return true;
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);
323 return true;
325 case REMOTE_0:
326 case REMOTE_1:
327 case REMOTE_2:
328 case REMOTE_3:
329 case REMOTE_4:
330 case REMOTE_5:
331 case REMOTE_6:
332 case REMOTE_7:
333 case REMOTE_8:
334 case REMOTE_9:
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());
347 return true;
350 break;
351 default:
352 break;
355 return false;
358 bool CSeekHandler::SeekTimeCode(const CAction &action)
360 if (m_timeCodePosition <= 0)
361 return false;
363 switch (action.GetID())
365 case ACTION_SELECT_ITEM:
366 case ACTION_PLAYER_PLAY:
367 case ACTION_PAUSE:
369 std::unique_lock<CCriticalSection> lock(m_critSection);
371 g_application.SeekTime(GetTimeCodeSeconds());
372 Reset();
373 return true;
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());
382 return true;
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());
390 return true;
392 default:
393 break;
395 return false;
398 void CSeekHandler::ChangeTimeCode(int remote)
400 if (remote >= ACTION_JUMP_SMS2 && remote <= ACTION_JUMP_SMS9)
402 // cast to REMOTE_X
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;
411 else
413 // rotate around
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
426 int tot = 0;
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;
433 int h = tot % 100;
435 return h * 3600 + m * 60 + s;
437 return 0;