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"
12 #include "FileItemList.h"
15 #include "cores/VideoPlayer/DVDFileInfo.h"
16 #include "filesystem/StackDirectory.h"
17 #include "utils/URIUtils.h"
18 #include "utils/log.h"
19 #include "video/VideoDatabase.h"
23 using namespace XFILE
;
25 CApplicationStackHelper::CApplicationStackHelper(void)
26 : m_currentStack(new CFileItemList
)
30 void CApplicationStackHelper::Clear()
32 m_currentStackPosition
= 0;
33 m_currentStack
->Clear();
36 void CApplicationStackHelper::OnPlayBackStarted(const CFileItem
& item
)
38 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
40 // time to clean up stack map
41 if (!HasRegisteredStack(item
))
45 auto stack
= GetRegisteredStack(item
);
46 Stackmap::iterator itr
= m_stackmap
.begin();
47 while (itr
!= m_stackmap
.end())
49 if (itr
->second
->m_pStack
!= stack
)
51 itr
= m_stackmap
.erase(itr
);
61 bool CApplicationStackHelper::InitializeStack(const CFileItem
& item
)
66 auto stack
= std::make_shared
<CFileItem
>(item
);
69 // read and determine kind of stack
71 CURL path
{item
.GetDynPath()};
72 if (!dir
.GetDirectory(path
, *m_currentStack
) || m_currentStack
->IsEmpty())
74 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
76 // keep cross-references between stack parts and the stack
77 SetRegisteredStack(GetStackPartFileItem(i
), stack
);
78 SetRegisteredStackPartNumber(GetStackPartFileItem(i
), i
);
80 m_currentStackIsDiscImageStack
= URIUtils::IsDiscImageStack(item
.GetDynPath());
85 std::optional
<int64_t> CApplicationStackHelper::InitializeStackStartPartAndOffset(
86 const CFileItem
& item
)
89 int64_t startoffset
= 0;
91 // case 1: stacked ISOs
92 if (m_currentStackIsDiscImageStack
)
94 // first assume values passed to the stack
95 int selectedFile
= item
.m_lStartPartNumber
;
96 startoffset
= item
.GetStartOffset();
98 // check if we instructed the stack to resume from default
99 if (startoffset
== STARTOFFSET_RESUME
) // selected file is not specified, pick the 'last' resume point
104 std::string path
= item
.GetDynPath();
105 if (item
.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item
.GetProperty("original_listitem_url").asString()))
106 path
= item
.GetProperty("original_listitem_url").asString();
107 if (dbs
.GetResumeBookMark(path
, bookmark
))
109 startoffset
= CUtil::ConvertSecsToMilliSecs(bookmark
.timeInSeconds
);
110 selectedFile
= bookmark
.partNumber
;
115 CLog::LogF(LOGERROR
, "Cannot open VideoDatabase");
118 // make sure that the selected part is within the boundaries
119 if (selectedFile
<= 0)
121 CLog::LogF(LOGWARNING
, "Selected part {} out of range, playing part 1", selectedFile
);
124 else if (selectedFile
> m_currentStack
->Size())
126 CLog::LogF(LOGWARNING
, "Selected part {} out of range, playing part {}", selectedFile
,
127 m_currentStack
->Size());
128 selectedFile
= m_currentStack
->Size();
131 // set startoffset in selected item, track stack item for updating purposes, and finally play disc part
132 m_currentStackPosition
= selectedFile
- 1;
133 startoffset
= startoffset
> 0 ? STARTOFFSET_RESUME
: 0;
135 // case 2: all other stacks
138 // see if we have the info in the database
139 //! @todo If user changes the time speed (FPS via framerate conversion stuff)
140 //! then these times will be wrong.
141 //! Also, this is really just a hack for the slow load up times we have
142 //! A much better solution is a fast reader of FPS and fileLength
143 //! that we can use on a file to get it's time.
144 std::vector
<uint64_t> times
;
145 bool haveTimes(false);
149 haveTimes
= dbs
.GetStackTimes(item
.GetDynPath(), times
);
153 // calculate the total time of the stack
154 uint64_t totalTimeMs
= 0;
155 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
159 // set end time in every part
160 GetStackPartFileItem(i
).SetEndOffset(times
[i
]);
165 if (!CDVDFileInfo::GetFileDuration(GetStackPartFileItem(i
).GetDynPath(), duration
))
167 m_currentStack
->Clear();
170 totalTimeMs
+= duration
;
171 // set end time in every part
172 GetStackPartFileItem(i
).SetEndOffset(totalTimeMs
);
173 times
.push_back(totalTimeMs
);
175 // set start time in every part
176 SetRegisteredStackPartStartTimeMs(GetStackPartFileItem(i
), GetStackPartStartTimeMs(i
));
178 // set total time in every part
179 totalTimeMs
= GetStackTotalTimeMs();
180 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
181 SetRegisteredStackTotalTimeMs(GetStackPartFileItem(i
), totalTimeMs
);
183 uint64_t msecs
= item
.GetStartOffset();
185 if (!haveTimes
|| item
.GetStartOffset() == STARTOFFSET_RESUME
)
189 // have our times now, so update the dB
190 if (!haveTimes
&& !times
.empty())
191 dbs
.SetStackTimes(item
.GetDynPath(), times
);
193 if (item
.GetStartOffset() == STARTOFFSET_RESUME
)
195 // can only resume seek here, not dvdstate
197 std::string path
= item
.GetDynPath();
198 if (item
.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item
.GetProperty("original_listitem_url").asString()))
199 path
= item
.GetProperty("original_listitem_url").asString();
200 if (dbs
.GetResumeBookMark(path
, bookmark
))
201 msecs
= static_cast<uint64_t>(bookmark
.timeInSeconds
* 1000);
209 m_currentStackPosition
= GetStackPartNumberAtTimeMs(msecs
);
210 startoffset
= msecs
- GetStackPartStartTimeMs(m_currentStackPosition
);
215 bool CApplicationStackHelper::IsPlayingISOStack() const
217 return m_currentStack
->Size() > 0 && m_currentStackIsDiscImageStack
;
220 bool CApplicationStackHelper::IsPlayingRegularStack() const
222 return m_currentStack
->Size() > 0 && !m_currentStackIsDiscImageStack
;
225 bool CApplicationStackHelper::HasNextStackPartFileItem() const
227 return m_currentStackPosition
< m_currentStack
->Size() - 1;
230 uint64_t CApplicationStackHelper::GetStackPartEndTimeMs(int partNumber
) const
232 return GetStackPartFileItem(partNumber
).GetEndOffset();
235 uint64_t CApplicationStackHelper::GetStackTotalTimeMs() const
237 return GetStackPartEndTimeMs(m_currentStack
->Size() - 1);
240 int CApplicationStackHelper::GetStackPartNumberAtTimeMs(uint64_t msecs
)
244 // work out where to seek to
245 for (int partNumber
= 0; partNumber
< m_currentStack
->Size(); partNumber
++)
247 if (msecs
< GetStackPartEndTimeMs(partNumber
))
254 void CApplicationStackHelper::ClearAllRegisteredStackInformation()
259 std::shared_ptr
<const CFileItem
> CApplicationStackHelper::GetRegisteredStack(
260 const CFileItem
& item
) const
262 return GetStackPartInformation(item
.GetDynPath())->m_pStack
;
265 bool CApplicationStackHelper::HasRegisteredStack(const CFileItem
& item
) const
267 const auto it
= m_stackmap
.find(item
.GetDynPath());
268 return it
!= m_stackmap
.end() && it
->second
!= nullptr;
271 void CApplicationStackHelper::SetRegisteredStack(const CFileItem
& item
,
272 std::shared_ptr
<CFileItem
> stackItem
)
274 GetStackPartInformation(item
.GetDynPath())->m_pStack
= std::move(stackItem
);
277 CFileItem
& CApplicationStackHelper::GetStackPartFileItem(int partNumber
)
279 return *(*m_currentStack
)[partNumber
];
282 const CFileItem
& CApplicationStackHelper::GetStackPartFileItem(int partNumber
) const
284 return *(*m_currentStack
)[partNumber
];
287 int CApplicationStackHelper::GetRegisteredStackPartNumber(const CFileItem
& item
)
289 return GetStackPartInformation(item
.GetDynPath())->m_lStackPartNumber
;
292 void CApplicationStackHelper::SetRegisteredStackPartNumber(const CFileItem
& item
, int partNumber
)
294 GetStackPartInformation(item
.GetDynPath())->m_lStackPartNumber
= partNumber
;
297 uint64_t CApplicationStackHelper::GetRegisteredStackPartStartTimeMs(const CFileItem
& item
) const
299 return GetStackPartInformation(item
.GetDynPath())->m_lStackPartStartTimeMs
;
302 void CApplicationStackHelper::SetRegisteredStackPartStartTimeMs(const CFileItem
& item
, uint64_t startTime
)
304 GetStackPartInformation(item
.GetDynPath())->m_lStackPartStartTimeMs
= startTime
;
307 uint64_t CApplicationStackHelper::GetRegisteredStackTotalTimeMs(const CFileItem
& item
) const
309 return GetStackPartInformation(item
.GetDynPath())->m_lStackTotalTimeMs
;
312 void CApplicationStackHelper::SetRegisteredStackTotalTimeMs(const CFileItem
& item
, uint64_t totalTime
)
314 GetStackPartInformation(item
.GetDynPath())->m_lStackTotalTimeMs
= totalTime
;
317 CApplicationStackHelper::StackPartInformationPtr
CApplicationStackHelper::GetStackPartInformation(
318 const std::string
& key
)
320 if (m_stackmap
.count(key
) == 0)
322 StackPartInformationPtr
value(new StackPartInformation());
323 m_stackmap
[key
] = value
;
325 return m_stackmap
[key
];
328 CApplicationStackHelper::StackPartInformationPtr
CApplicationStackHelper::GetStackPartInformation(
329 const std::string
& key
) const
331 const auto it
= m_stackmap
.find(key
);
332 if (it
== m_stackmap
.end())
333 return std::make_shared
<StackPartInformation
>();