2 * Copyright (C) 2017-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 "ApplicationStackHelper.h"
11 #include "ApplicationPlayer.h"
13 #include "cores/VideoPlayer/DVDFileInfo.h"
14 #include "filesystem/StackDirectory.h"
15 #include "utils/URIUtils.h"
16 #include "utils/log.h"
17 #include "video/VideoDatabase.h"
21 using namespace XFILE
;
23 CApplicationStackHelper::CApplicationStackHelper(void)
24 : m_currentStack(new CFileItemList
)
28 void CApplicationStackHelper::Clear()
30 m_currentStackPosition
= 0;
31 m_currentStack
->Clear();
34 void CApplicationStackHelper::OnPlayBackStarted(const CFileItem
& item
)
36 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
38 // time to clean up stack map
39 if (!HasRegisteredStack(item
))
43 auto stack
= GetRegisteredStack(item
);
44 Stackmap::iterator itr
= m_stackmap
.begin();
45 while (itr
!= m_stackmap
.end())
47 if (itr
->second
->m_pStack
!= stack
)
49 itr
= m_stackmap
.erase(itr
);
59 bool CApplicationStackHelper::InitializeStack(const CFileItem
& item
)
64 CFileItemPtr
stack(new CFileItem(item
));
67 // read and determine kind of stack
69 if (!dir
.GetDirectory(item
.GetURL(), *m_currentStack
) || m_currentStack
->IsEmpty())
71 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
73 // keep cross-references between stack parts and the stack
74 SetRegisteredStack(GetStackPartFileItem(i
), stack
);
75 SetRegisteredStackPartNumber(GetStackPartFileItem(i
), i
);
77 m_currentStackIsDiscImageStack
= CFileItem(CStackDirectory::GetFirstStackedFile(item
.GetPath()), false).IsDiscImage();
82 int CApplicationStackHelper::InitializeStackStartPartAndOffset(const CFileItem
& item
)
85 int64_t startoffset
= 0;
87 // case 1: stacked ISOs
88 if (m_currentStackIsDiscImageStack
)
90 // first assume values passed to the stack
91 int selectedFile
= item
.m_lStartPartNumber
;
92 startoffset
= item
.GetStartOffset();
94 // check if we instructed the stack to resume from default
95 if (startoffset
== STARTOFFSET_RESUME
) // selected file is not specified, pick the 'last' resume point
100 std::string path
= item
.GetPath();
101 if (item
.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item
.GetProperty("original_listitem_url").asString()))
102 path
= item
.GetProperty("original_listitem_url").asString();
103 if (dbs
.GetResumeBookMark(path
, bookmark
))
105 startoffset
= CUtil::ConvertSecsToMilliSecs(bookmark
.timeInSeconds
);
106 selectedFile
= bookmark
.partNumber
;
111 CLog::LogF(LOGERROR
, "Cannot open VideoDatabase");
114 // make sure that the selected part is within the boundaries
115 if (selectedFile
<= 0)
117 CLog::LogF(LOGWARNING
, "Selected part {} out of range, playing part 1", selectedFile
);
120 else if (selectedFile
> m_currentStack
->Size())
122 CLog::LogF(LOGWARNING
, "Selected part {} out of range, playing part {}", selectedFile
,
123 m_currentStack
->Size());
124 selectedFile
= m_currentStack
->Size();
127 // set startoffset in selected item, track stack item for updating purposes, and finally play disc part
128 m_currentStackPosition
= selectedFile
- 1;
129 startoffset
= startoffset
> 0 ? STARTOFFSET_RESUME
: 0;
131 // case 2: all other stacks
134 // see if we have the info in the database
135 //! @todo If user changes the time speed (FPS via framerate conversion stuff)
136 //! then these times will be wrong.
137 //! Also, this is really just a hack for the slow load up times we have
138 //! A much better solution is a fast reader of FPS and fileLength
139 //! that we can use on a file to get it's time.
140 std::vector
<uint64_t> times
;
141 bool haveTimes(false);
145 haveTimes
= dbs
.GetStackTimes(item
.GetPath(), times
);
149 // calculate the total time of the stack
150 uint64_t totalTimeMs
= 0;
151 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
155 // set end time in every part
156 GetStackPartFileItem(i
).SetEndOffset(times
[i
]);
161 if (!CDVDFileInfo::GetFileDuration(GetStackPartFileItem(i
).GetPath(), duration
))
163 m_currentStack
->Clear();
166 totalTimeMs
+= duration
;
167 // set end time in every part
168 GetStackPartFileItem(i
).SetEndOffset(totalTimeMs
);
169 times
.push_back(totalTimeMs
);
171 // set start time in every part
172 SetRegisteredStackPartStartTimeMs(GetStackPartFileItem(i
), GetStackPartStartTimeMs(i
));
174 // set total time in every part
175 totalTimeMs
= GetStackTotalTimeMs();
176 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
177 SetRegisteredStackTotalTimeMs(GetStackPartFileItem(i
), totalTimeMs
);
179 uint64_t msecs
= item
.GetStartOffset();
181 if (!haveTimes
|| item
.GetStartOffset() == STARTOFFSET_RESUME
)
185 // have our times now, so update the dB
186 if (!haveTimes
&& !times
.empty())
187 dbs
.SetStackTimes(item
.GetPath(), times
);
189 if (item
.GetStartOffset() == STARTOFFSET_RESUME
)
191 // can only resume seek here, not dvdstate
193 std::string path
= item
.GetPath();
194 if (item
.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item
.GetProperty("original_listitem_url").asString()))
195 path
= item
.GetProperty("original_listitem_url").asString();
196 if (dbs
.GetResumeBookMark(path
, bookmark
))
197 msecs
= static_cast<uint64_t>(bookmark
.timeInSeconds
* 1000);
205 m_currentStackPosition
= GetStackPartNumberAtTimeMs(msecs
);
206 startoffset
= msecs
- GetStackPartStartTimeMs(m_currentStackPosition
);
211 int CApplicationStackHelper::GetStackPartNumberAtTimeMs(uint64_t msecs
)
215 // work out where to seek to
216 for (int partNumber
= 0; partNumber
< m_currentStack
->Size(); partNumber
++)
218 if (msecs
< GetStackPartEndTimeMs(partNumber
))
225 void CApplicationStackHelper::ClearAllRegisteredStackInformation()
230 std::shared_ptr
<const CFileItem
> CApplicationStackHelper::GetRegisteredStack(
231 const CFileItem
& item
) const
233 return GetStackPartInformation(item
.GetPath())->m_pStack
;
236 bool CApplicationStackHelper::HasRegisteredStack(const CFileItem
& item
) const
238 const auto it
= m_stackmap
.find(item
.GetPath());
239 return it
!= m_stackmap
.end() && it
->second
!= nullptr;
242 void CApplicationStackHelper::SetRegisteredStack(const CFileItem
& item
, CFileItemPtr stackItem
)
244 GetStackPartInformation(item
.GetPath())->m_pStack
= std::move(stackItem
);
247 int CApplicationStackHelper::GetRegisteredStackPartNumber(const CFileItem
& item
)
249 return GetStackPartInformation(item
.GetPath())->m_lStackPartNumber
;
252 void CApplicationStackHelper::SetRegisteredStackPartNumber(const CFileItem
& item
, int partNumber
)
254 GetStackPartInformation(item
.GetPath())->m_lStackPartNumber
= partNumber
;
257 uint64_t CApplicationStackHelper::GetRegisteredStackPartStartTimeMs(const CFileItem
& item
) const
259 return GetStackPartInformation(item
.GetPath())->m_lStackPartStartTimeMs
;
262 void CApplicationStackHelper::SetRegisteredStackPartStartTimeMs(const CFileItem
& item
, uint64_t startTime
)
264 GetStackPartInformation(item
.GetPath())->m_lStackPartStartTimeMs
= startTime
;
267 uint64_t CApplicationStackHelper::GetRegisteredStackTotalTimeMs(const CFileItem
& item
) const
269 return GetStackPartInformation(item
.GetPath())->m_lStackTotalTimeMs
;
272 void CApplicationStackHelper::SetRegisteredStackTotalTimeMs(const CFileItem
& item
, uint64_t totalTime
)
274 GetStackPartInformation(item
.GetPath())->m_lStackTotalTimeMs
= totalTime
;
277 CApplicationStackHelper::StackPartInformationPtr
CApplicationStackHelper::GetStackPartInformation(
278 const std::string
& key
)
280 if (m_stackmap
.count(key
) == 0)
282 StackPartInformationPtr
value(new StackPartInformation());
283 m_stackmap
[key
] = value
;
285 return m_stackmap
[key
];
288 CApplicationStackHelper::StackPartInformationPtr
CApplicationStackHelper::GetStackPartInformation(
289 const std::string
& key
) const
291 const auto it
= m_stackmap
.find(key
);
292 if (it
== m_stackmap
.end())
293 return std::make_shared
<StackPartInformation
>();