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"
14 #include "cores/VideoPlayer/DVDFileInfo.h"
15 #include "filesystem/StackDirectory.h"
16 #include "utils/URIUtils.h"
17 #include "utils/log.h"
18 #include "video/VideoDatabase.h"
22 using namespace XFILE
;
24 CApplicationStackHelper::CApplicationStackHelper(void)
25 : m_currentStack(new CFileItemList
)
29 void CApplicationStackHelper::Clear()
31 m_currentStackPosition
= 0;
32 m_currentStack
->Clear();
35 void CApplicationStackHelper::OnPlayBackStarted(const CFileItem
& item
)
37 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
39 // time to clean up stack map
40 if (!HasRegisteredStack(item
))
44 auto stack
= GetRegisteredStack(item
);
45 Stackmap::iterator itr
= m_stackmap
.begin();
46 while (itr
!= m_stackmap
.end())
48 if (itr
->second
->m_pStack
!= stack
)
50 itr
= m_stackmap
.erase(itr
);
60 bool CApplicationStackHelper::InitializeStack(const CFileItem
& item
)
65 auto stack
= std::make_shared
<CFileItem
>(item
);
68 // read and determine kind of stack
70 if (!dir
.GetDirectory(item
.GetURL(), *m_currentStack
) || m_currentStack
->IsEmpty())
72 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
74 // keep cross-references between stack parts and the stack
75 SetRegisteredStack(GetStackPartFileItem(i
), stack
);
76 SetRegisteredStackPartNumber(GetStackPartFileItem(i
), i
);
78 m_currentStackIsDiscImageStack
= CFileItem(CStackDirectory::GetFirstStackedFile(item
.GetPath()), false).IsDiscImage();
83 int CApplicationStackHelper::InitializeStackStartPartAndOffset(const CFileItem
& item
)
86 int64_t startoffset
= 0;
88 // case 1: stacked ISOs
89 if (m_currentStackIsDiscImageStack
)
91 // first assume values passed to the stack
92 int selectedFile
= item
.m_lStartPartNumber
;
93 startoffset
= item
.GetStartOffset();
95 // check if we instructed the stack to resume from default
96 if (startoffset
== STARTOFFSET_RESUME
) // selected file is not specified, pick the 'last' resume point
101 std::string path
= item
.GetPath();
102 if (item
.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item
.GetProperty("original_listitem_url").asString()))
103 path
= item
.GetProperty("original_listitem_url").asString();
104 if (dbs
.GetResumeBookMark(path
, bookmark
))
106 startoffset
= CUtil::ConvertSecsToMilliSecs(bookmark
.timeInSeconds
);
107 selectedFile
= bookmark
.partNumber
;
112 CLog::LogF(LOGERROR
, "Cannot open VideoDatabase");
115 // make sure that the selected part is within the boundaries
116 if (selectedFile
<= 0)
118 CLog::LogF(LOGWARNING
, "Selected part {} out of range, playing part 1", selectedFile
);
121 else if (selectedFile
> m_currentStack
->Size())
123 CLog::LogF(LOGWARNING
, "Selected part {} out of range, playing part {}", selectedFile
,
124 m_currentStack
->Size());
125 selectedFile
= m_currentStack
->Size();
128 // set startoffset in selected item, track stack item for updating purposes, and finally play disc part
129 m_currentStackPosition
= selectedFile
- 1;
130 startoffset
= startoffset
> 0 ? STARTOFFSET_RESUME
: 0;
132 // case 2: all other stacks
135 // see if we have the info in the database
136 //! @todo If user changes the time speed (FPS via framerate conversion stuff)
137 //! then these times will be wrong.
138 //! Also, this is really just a hack for the slow load up times we have
139 //! A much better solution is a fast reader of FPS and fileLength
140 //! that we can use on a file to get it's time.
141 std::vector
<uint64_t> times
;
142 bool haveTimes(false);
146 haveTimes
= dbs
.GetStackTimes(item
.GetPath(), times
);
150 // calculate the total time of the stack
151 uint64_t totalTimeMs
= 0;
152 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
156 // set end time in every part
157 GetStackPartFileItem(i
).SetEndOffset(times
[i
]);
162 if (!CDVDFileInfo::GetFileDuration(GetStackPartFileItem(i
).GetPath(), duration
))
164 m_currentStack
->Clear();
167 totalTimeMs
+= duration
;
168 // set end time in every part
169 GetStackPartFileItem(i
).SetEndOffset(totalTimeMs
);
170 times
.push_back(totalTimeMs
);
172 // set start time in every part
173 SetRegisteredStackPartStartTimeMs(GetStackPartFileItem(i
), GetStackPartStartTimeMs(i
));
175 // set total time in every part
176 totalTimeMs
= GetStackTotalTimeMs();
177 for (int i
= 0; i
< m_currentStack
->Size(); i
++)
178 SetRegisteredStackTotalTimeMs(GetStackPartFileItem(i
), totalTimeMs
);
180 uint64_t msecs
= item
.GetStartOffset();
182 if (!haveTimes
|| item
.GetStartOffset() == STARTOFFSET_RESUME
)
186 // have our times now, so update the dB
187 if (!haveTimes
&& !times
.empty())
188 dbs
.SetStackTimes(item
.GetPath(), times
);
190 if (item
.GetStartOffset() == STARTOFFSET_RESUME
)
192 // can only resume seek here, not dvdstate
194 std::string path
= item
.GetPath();
195 if (item
.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item
.GetProperty("original_listitem_url").asString()))
196 path
= item
.GetProperty("original_listitem_url").asString();
197 if (dbs
.GetResumeBookMark(path
, bookmark
))
198 msecs
= static_cast<uint64_t>(bookmark
.timeInSeconds
* 1000);
206 m_currentStackPosition
= GetStackPartNumberAtTimeMs(msecs
);
207 startoffset
= msecs
- GetStackPartStartTimeMs(m_currentStackPosition
);
212 bool CApplicationStackHelper::IsPlayingISOStack() const
214 return m_currentStack
->Size() > 0 && m_currentStackIsDiscImageStack
;
217 bool CApplicationStackHelper::IsPlayingRegularStack() const
219 return m_currentStack
->Size() > 0 && !m_currentStackIsDiscImageStack
;
222 bool CApplicationStackHelper::HasNextStackPartFileItem() const
224 return m_currentStackPosition
< m_currentStack
->Size() - 1;
227 uint64_t CApplicationStackHelper::GetStackPartEndTimeMs(int partNumber
) const
229 return GetStackPartFileItem(partNumber
).GetEndOffset();
232 uint64_t CApplicationStackHelper::GetStackTotalTimeMs() const
234 return GetStackPartEndTimeMs(m_currentStack
->Size() - 1);
237 int CApplicationStackHelper::GetStackPartNumberAtTimeMs(uint64_t msecs
)
241 // work out where to seek to
242 for (int partNumber
= 0; partNumber
< m_currentStack
->Size(); partNumber
++)
244 if (msecs
< GetStackPartEndTimeMs(partNumber
))
251 void CApplicationStackHelper::ClearAllRegisteredStackInformation()
256 std::shared_ptr
<const CFileItem
> CApplicationStackHelper::GetRegisteredStack(
257 const CFileItem
& item
) const
259 return GetStackPartInformation(item
.GetPath())->m_pStack
;
262 bool CApplicationStackHelper::HasRegisteredStack(const CFileItem
& item
) const
264 const auto it
= m_stackmap
.find(item
.GetPath());
265 return it
!= m_stackmap
.end() && it
->second
!= nullptr;
268 void CApplicationStackHelper::SetRegisteredStack(const CFileItem
& item
,
269 std::shared_ptr
<CFileItem
> stackItem
)
271 GetStackPartInformation(item
.GetPath())->m_pStack
= std::move(stackItem
);
274 CFileItem
& CApplicationStackHelper::GetStackPartFileItem(int partNumber
)
276 return *(*m_currentStack
)[partNumber
];
279 const CFileItem
& CApplicationStackHelper::GetStackPartFileItem(int partNumber
) const
281 return *(*m_currentStack
)[partNumber
];
284 int CApplicationStackHelper::GetRegisteredStackPartNumber(const CFileItem
& item
)
286 return GetStackPartInformation(item
.GetPath())->m_lStackPartNumber
;
289 void CApplicationStackHelper::SetRegisteredStackPartNumber(const CFileItem
& item
, int partNumber
)
291 GetStackPartInformation(item
.GetPath())->m_lStackPartNumber
= partNumber
;
294 uint64_t CApplicationStackHelper::GetRegisteredStackPartStartTimeMs(const CFileItem
& item
) const
296 return GetStackPartInformation(item
.GetPath())->m_lStackPartStartTimeMs
;
299 void CApplicationStackHelper::SetRegisteredStackPartStartTimeMs(const CFileItem
& item
, uint64_t startTime
)
301 GetStackPartInformation(item
.GetPath())->m_lStackPartStartTimeMs
= startTime
;
304 uint64_t CApplicationStackHelper::GetRegisteredStackTotalTimeMs(const CFileItem
& item
) const
306 return GetStackPartInformation(item
.GetPath())->m_lStackTotalTimeMs
;
309 void CApplicationStackHelper::SetRegisteredStackTotalTimeMs(const CFileItem
& item
, uint64_t totalTime
)
311 GetStackPartInformation(item
.GetPath())->m_lStackTotalTimeMs
= totalTime
;
314 CApplicationStackHelper::StackPartInformationPtr
CApplicationStackHelper::GetStackPartInformation(
315 const std::string
& key
)
317 if (m_stackmap
.count(key
) == 0)
319 StackPartInformationPtr
value(new StackPartInformation());
320 m_stackmap
[key
] = value
;
322 return m_stackmap
[key
];
325 CApplicationStackHelper::StackPartInformationPtr
CApplicationStackHelper::GetStackPartInformation(
326 const std::string
& key
) const
328 const auto it
= m_stackmap
.find(key
);
329 if (it
== m_stackmap
.end())
330 return std::make_shared
<StackPartInformation
>();