2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
22 #include "CacheStrategy.h"
24 #include "PlatformInclude.h"
27 #include "utils/log.h"
28 #include "threads/SingleLock.h"
29 #include "utils/TimeUtils.h"
30 #include "SpecialProtocol.h"
32 #include "PlatformDefs.h" //for PRIdS, PRId64
35 using namespace XFILE;
37 CCacheStrategy::CCacheStrategy() : m_bEndOfInput(false)
42 CCacheStrategy::~CCacheStrategy()
46 void CCacheStrategy::EndOfInput() {
50 bool CCacheStrategy::IsEndOfInput()
55 void CCacheStrategy::ClearEndOfInput()
57 m_bEndOfInput = false;
60 CSimpleFileCache::CSimpleFileCache()
61 : m_hCacheFileRead(NULL)
62 , m_hCacheFileWrite(NULL)
63 , m_hDataAvailEvent(NULL)
66 , m_nReadPosition(0) {
69 CSimpleFileCache::~CSimpleFileCache()
74 int CSimpleFileCache::Open()
78 m_hDataAvailEvent = new CEvent;
80 CStdString fileName = CSpecialProtocol::TranslatePath(CUtil::GetNextFilename("special://temp/filecache%03d.cache", 999));
83 CLog::Log(LOGERROR, "%s - Unable to generate a new filename", __FUNCTION__);
85 return CACHE_RC_ERROR;
88 m_hCacheFileWrite = CreateFile(fileName.c_str()
89 , GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE
92 , FILE_ATTRIBUTE_NORMAL
95 if(m_hCacheFileWrite == INVALID_HANDLE_VALUE)
97 CLog::Log(LOGERROR, "%s - failed to create file %s with error code %d", __FUNCTION__, fileName.c_str(), GetLastError());
99 return CACHE_RC_ERROR;
102 m_hCacheFileRead = CreateFile(fileName.c_str()
103 , GENERIC_READ, FILE_SHARE_WRITE
106 , FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE
109 if(m_hCacheFileRead == INVALID_HANDLE_VALUE)
111 CLog::Log(LOGERROR, "%s - failed to open file %s with error code %d", __FUNCTION__, fileName.c_str(), GetLastError());
113 return CACHE_RC_ERROR;
119 void CSimpleFileCache::Close()
121 if (m_hDataAvailEvent)
122 delete m_hDataAvailEvent;
124 m_hDataAvailEvent = NULL;
126 if (m_hCacheFileWrite)
127 CloseHandle(m_hCacheFileWrite);
129 m_hCacheFileWrite = NULL;
131 if (m_hCacheFileRead)
132 CloseHandle(m_hCacheFileRead);
134 m_hCacheFileRead = NULL;
137 int CSimpleFileCache::WriteToCache(const char *pBuffer, size_t iSize)
140 if (!WriteFile(m_hCacheFileWrite, pBuffer, iSize, &iWritten, NULL))
142 CLog::Log(LOGERROR, "%s - failed to write to file. err: %u",
143 __FUNCTION__, GetLastError());
144 return CACHE_RC_ERROR;
147 m_nWritePosition += iWritten;
149 // when reader waits for data it will wait on the event.
150 m_hDataAvailEvent->Set();
155 int64_t CSimpleFileCache::GetAvailableRead()
157 return m_nWritePosition - m_nReadPosition;
160 int CSimpleFileCache::ReadFromCache(char *pBuffer, size_t iMaxSize)
162 int64_t iAvailable = GetAvailableRead();
163 if ( iAvailable <= 0 ) {
164 return m_bEndOfInput? 0 : CACHE_RC_WOULD_BLOCK;
167 if (iMaxSize > (size_t)iAvailable)
168 iMaxSize = (size_t)iAvailable;
171 if (!ReadFile(m_hCacheFileRead, pBuffer, iMaxSize, &iRead, NULL)) {
172 CLog::Log(LOGERROR,"CSimpleFileCache::ReadFromCache - failed to read %"PRIdS" bytes.", iMaxSize);
173 return CACHE_RC_ERROR;
175 m_nReadPosition += iRead;
183 int64_t CSimpleFileCache::WaitForData(unsigned int iMinAvail, unsigned int iMillis)
185 if( iMillis == 0 || IsEndOfInput() )
186 return GetAvailableRead();
188 XbmcThreads::EndTime endTime(iMillis);
189 while (!IsEndOfInput())
191 int64_t iAvail = GetAvailableRead();
192 if (iAvail >= iMinAvail)
195 if (!m_hDataAvailEvent->WaitMSec(endTime.MillisLeft()))
196 return CACHE_RC_TIMEOUT;
198 return GetAvailableRead();
201 int64_t CSimpleFileCache::Seek(int64_t iFilePosition)
203 int64_t iTarget = iFilePosition - m_nStartPosition;
207 CLog::Log(LOGDEBUG,"CSimpleFileCache::Seek, request seek before start of cache.");
208 return CACHE_RC_ERROR;
211 int64_t nDiff = iTarget - m_nWritePosition;
212 if (nDiff > 500000 || (nDiff > 0 && WaitForData((unsigned int)(iTarget - m_nReadPosition), 5000) == CACHE_RC_TIMEOUT))
214 CLog::Log(LOGDEBUG,"CSimpleFileCache::Seek - Attempt to seek past read data");
215 return CACHE_RC_ERROR;
219 pos.QuadPart = iTarget;
221 if(!SetFilePointerEx(m_hCacheFileRead, pos, NULL, FILE_BEGIN))
222 return CACHE_RC_ERROR;
224 m_nReadPosition = iTarget;
227 return iFilePosition;
230 void CSimpleFileCache::Reset(int64_t iSourcePosition, bool clearAnyway)
233 if (!clearAnyway && IsCachedPosition(iSourcePosition))
235 pos.QuadPart = m_nReadPosition = iSourcePosition - m_nStartPosition;
236 SetFilePointerEx(m_hCacheFileRead, pos, NULL, FILE_BEGIN);
242 SetFilePointerEx(m_hCacheFileWrite, pos, NULL, FILE_BEGIN);
243 SetFilePointerEx(m_hCacheFileRead, pos, NULL, FILE_BEGIN);
244 m_nStartPosition = iSourcePosition;
246 m_nWritePosition = 0;
249 void CSimpleFileCache::EndOfInput()
251 CCacheStrategy::EndOfInput();
252 m_hDataAvailEvent->Set();
255 int64_t CSimpleFileCache::CachedDataEndPosIfSeekTo(int64_t iFilePosition)
257 if (iFilePosition >= m_nStartPosition && iFilePosition <= m_nStartPosition + m_nWritePosition)
258 return m_nStartPosition + m_nWritePosition;
259 return iFilePosition;
262 int64_t CSimpleFileCache::CachedDataEndPos()
264 return m_nStartPosition + m_nWritePosition;
267 bool CSimpleFileCache::IsCachedPosition(int64_t iFilePosition)
269 return iFilePosition >= m_nStartPosition && iFilePosition <= m_nStartPosition + m_nWritePosition;
272 CCacheStrategy *CSimpleFileCache::CreateNew()
274 return new CSimpleFileCache();
278 CSimpleDoubleCache::CSimpleDoubleCache(CCacheStrategy *impl)
280 assert(NULL != impl);
285 CSimpleDoubleCache::~CSimpleDoubleCache()
291 int CSimpleDoubleCache::Open()
293 return m_pCache->Open();
296 void CSimpleDoubleCache::Close()
306 int CSimpleDoubleCache::WriteToCache(const char *pBuffer, size_t iSize)
308 return m_pCache->WriteToCache(pBuffer, iSize);
311 int CSimpleDoubleCache::ReadFromCache(char *pBuffer, size_t iMaxSize)
313 return m_pCache->ReadFromCache(pBuffer, iMaxSize);
316 int64_t CSimpleDoubleCache::WaitForData(unsigned int iMinAvail, unsigned int iMillis)
318 return m_pCache->WaitForData(iMinAvail, iMillis);
321 int64_t CSimpleDoubleCache::Seek(int64_t iFilePosition)
323 return m_pCache->Seek(iFilePosition);
326 void CSimpleDoubleCache::Reset(int64_t iSourcePosition, bool clearAnyway)
328 if (!clearAnyway && m_pCache->IsCachedPosition(iSourcePosition)
329 && (!m_pCacheOld || !m_pCacheOld->IsCachedPosition(iSourcePosition)
330 || m_pCache->CachedDataEndPos() >= m_pCacheOld->CachedDataEndPos()))
332 m_pCache->Reset(iSourcePosition, clearAnyway);
337 CCacheStrategy *pCacheNew = m_pCache->CreateNew();
338 if (pCacheNew->Open() != CACHE_RC_OK)
341 m_pCache->Reset(iSourcePosition, clearAnyway);
344 pCacheNew->Reset(iSourcePosition, clearAnyway);
345 m_pCacheOld = m_pCache;
346 m_pCache = pCacheNew;
349 m_pCacheOld->Reset(iSourcePosition, clearAnyway);
350 CCacheStrategy *tmp = m_pCacheOld;
351 m_pCacheOld = m_pCache;
355 void CSimpleDoubleCache::EndOfInput()
357 m_pCache->EndOfInput();
360 bool CSimpleDoubleCache::IsEndOfInput()
362 return m_pCache->IsEndOfInput();
365 void CSimpleDoubleCache::ClearEndOfInput()
367 m_pCache->ClearEndOfInput();
370 int64_t CSimpleDoubleCache::CachedDataEndPos()
372 return m_pCache->CachedDataEndPos();
375 int64_t CSimpleDoubleCache::CachedDataEndPosIfSeekTo(int64_t iFilePosition)
377 int64_t ret = m_pCache->CachedDataEndPosIfSeekTo(iFilePosition);
379 return std::max(ret, m_pCacheOld->CachedDataEndPosIfSeekTo(iFilePosition));
383 bool CSimpleDoubleCache::IsCachedPosition(int64_t iFilePosition)
385 return m_pCache->IsCachedPosition(iFilePosition) || (m_pCacheOld && m_pCacheOld->IsCachedPosition(iFilePosition));
388 CCacheStrategy *CSimpleDoubleCache::CreateNew()
390 return new CSimpleDoubleCache(m_pCache->CreateNew());