changed: allow cache buffer size to be configured via advancedsettings rather than...
[xbmc:xbmc-antiquated.git] / xbmc / FileSystem / CacheMemBuffer.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #ifdef _LINUX
23 #include "../linux/PlatformDefs.h"
24 #endif
25 #include "AdvancedSettings.h"
26 #include "CacheMemBuffer.h"
27 #include "utils/log.h"
28 #include "utils/SingleLock.h"
29 #include "utils/TimeUtils.h"
30
31 #include <math.h>
32
33 using namespace XFILE;
34
35 #define SEEK_CHECK_RET(x) if (!(x)) return -1;
36
37 CacheMemBuffer::CacheMemBuffer()
38  : CCacheStrategy()
39 {
40   m_nStartPosition = 0;
41   m_buffer.Create(g_advancedSettings.m_cacheMemBufferSize + 1);
42   m_HistoryBuffer.Create(g_advancedSettings.m_cacheMemBufferSize + 1);
43   m_forwardBuffer.Create(g_advancedSettings.m_cacheMemBufferSize + 1);
44 }
45
46
47 CacheMemBuffer::~CacheMemBuffer()
48 {
49   m_buffer.Destroy();
50   m_HistoryBuffer.Destroy();
51   m_forwardBuffer.Destroy();
52 }
53
54 int CacheMemBuffer::Open()
55 {
56   m_nStartPosition = 0;
57   m_buffer.Clear();
58   m_HistoryBuffer.Clear();
59   m_forwardBuffer.Clear();
60   return CACHE_RC_OK;
61 }
62
63 int CacheMemBuffer::Close()
64 {
65   m_buffer.Clear();
66   m_HistoryBuffer.Clear();
67   m_forwardBuffer.Clear();
68   return CACHE_RC_OK;
69 }
70
71 int CacheMemBuffer::WriteToCache(const char *pBuffer, size_t iSize)
72 {
73   CSingleLock lock(m_sync);
74   unsigned int nToWrite = m_buffer.getMaxWriteSize() ;
75
76   // must also check the forward buffer.
77   // if we have leftovers from the previous seek - we need not read anymore until they are utilized
78   if (nToWrite == 0 || m_forwardBuffer.getMaxReadSize() > 0)
79     return 0;
80
81   if (nToWrite > iSize)
82     nToWrite = iSize;
83
84   if (!m_buffer.WriteData((char*)pBuffer, nToWrite))
85   {
86     CLog::Log(LOGWARNING,"%s, failed to write %d bytes to buffer. max buffer size: %d", __FUNCTION__, nToWrite, m_buffer.getMaxWriteSize());
87     nToWrite = 0;
88   }
89
90   m_written.Set();
91
92   return nToWrite;
93 }
94
95 int CacheMemBuffer::ReadFromCache(char *pBuffer, size_t iMaxSize)
96 {
97   CSingleLock lock(m_sync);
98   if ( m_buffer.getMaxReadSize() == 0 ) {
99     return m_bEndOfInput?CACHE_RC_EOF : CACHE_RC_WOULD_BLOCK;
100   }
101
102   int nRead = iMaxSize;
103   if ((size_t) m_buffer.getMaxReadSize() < iMaxSize)
104     nRead = m_buffer.getMaxReadSize();
105
106   if (nRead > 0)
107   {
108     if (!m_buffer.ReadData(pBuffer, nRead))
109     {
110       CLog::Log(LOGWARNING, "%s, failed to read %d bytes from buffer. max read size: %d", __FUNCTION__, nRead, m_buffer.getMaxReadSize());
111       return 0;
112     }
113
114     // copy to history so we can seek back
115     if ((int) m_HistoryBuffer.getMaxWriteSize() < nRead)
116       m_HistoryBuffer.SkipBytes(nRead);
117     m_HistoryBuffer.WriteData(pBuffer, nRead);
118
119     m_nStartPosition += nRead;
120   }
121
122   // check forward buffer and copy it when enough space is available
123   if (m_forwardBuffer.getMaxReadSize() > 0 && m_buffer.getMaxWriteSize() >= m_forwardBuffer.getMaxReadSize())
124   {
125     m_buffer.Append(m_forwardBuffer);
126     m_forwardBuffer.Clear();
127   }
128
129   if (nRead > 0)
130     m_space.Set();
131
132   return nRead;
133 }
134
135 int64_t CacheMemBuffer::WaitForData(unsigned int iMinAvail, unsigned int millis)
136 {
137   if (millis == 0 || IsEndOfInput())
138     return m_buffer.getMaxReadSize();
139
140   unsigned int time = CTimeUtils::GetTimeMS() + millis;
141   while (!IsEndOfInput() && (unsigned int) m_buffer.getMaxReadSize() < iMinAvail && CTimeUtils::GetTimeMS() < time )
142     m_written.WaitMSec(50); // may miss the deadline. shouldn't be a problem.
143
144   return m_buffer.getMaxReadSize();
145 }
146
147 int64_t CacheMemBuffer::Seek(int64_t iFilePosition, int iWhence)
148 {
149   if (iWhence != SEEK_SET)
150   {
151     // sanity. we should always get here with SEEK_SET
152     CLog::Log(LOGERROR, "%s, only SEEK_SET supported.", __FUNCTION__);
153     return CACHE_RC_ERROR;
154   }
155
156   CSingleLock lock(m_sync);
157
158   // if seek is a bit over what we have, try to wait a few seconds for the data to be available.
159   // we try to avoid a (heavy) seek on the source
160   if (iFilePosition > m_nStartPosition + m_buffer.getMaxReadSize() &&
161       iFilePosition < m_nStartPosition + m_buffer.getMaxReadSize() + 100000)
162   {
163     int nRequired = (int)(iFilePosition - (m_nStartPosition + m_buffer.getMaxReadSize()));
164     lock.Leave();
165     WaitForData(nRequired + 1, 5000);
166     lock.Enter();
167   }
168
169   // check if seek is inside the current buffer
170   if (iFilePosition >= m_nStartPosition && iFilePosition < m_nStartPosition + m_buffer.getMaxReadSize())
171   {
172     unsigned int nOffset = (iFilePosition - m_nStartPosition);
173     // copy to history so we can seek back
174     if (m_HistoryBuffer.getMaxWriteSize() < nOffset)
175       m_HistoryBuffer.SkipBytes(nOffset);
176
177     if (!m_buffer.ReadData(m_HistoryBuffer, nOffset))
178     {
179       CLog::Log(LOGERROR, "%s, failed to copy %d bytes to history", __FUNCTION__, nOffset);
180     }
181
182     m_nStartPosition = iFilePosition;
183     m_space.Set();
184     return m_nStartPosition;
185   }
186
187   int64_t iHistoryStart = m_nStartPosition - m_HistoryBuffer.getMaxReadSize();
188   if (iFilePosition < m_nStartPosition && iFilePosition >= iHistoryStart)
189   {
190     CRingBuffer saveHist, saveUnRead;
191     int64_t nToSkip = iFilePosition - iHistoryStart;
192     SEEK_CHECK_RET(m_HistoryBuffer.ReadData(saveHist, (int)nToSkip));
193
194     SEEK_CHECK_RET(saveUnRead.Copy(m_buffer));
195
196     SEEK_CHECK_RET(m_buffer.Copy(m_HistoryBuffer));
197     int nSpace = m_buffer.getMaxWriteSize();
198     int nToCopy = saveUnRead.getMaxReadSize();
199
200     if (nToCopy < nSpace)
201       nSpace = nToCopy;
202
203     SEEK_CHECK_RET(saveUnRead.ReadData(m_buffer, nSpace));
204     nToCopy -= nSpace;
205     if (nToCopy > 0)
206       m_forwardBuffer.Copy(saveUnRead);
207
208     SEEK_CHECK_RET(m_HistoryBuffer.Copy(saveHist));
209     m_HistoryBuffer.Clear();
210
211     m_nStartPosition = iFilePosition;
212     m_space.Set();
213     return m_nStartPosition;
214   }
215
216   // seek outside the buffer. return error.
217   return CACHE_RC_ERROR;
218 }
219
220 void CacheMemBuffer::Reset(int64_t iSourcePosition)
221 {
222   CSingleLock lock(m_sync);
223   m_nStartPosition = iSourcePosition;
224   m_buffer.Clear();
225   m_HistoryBuffer.Clear();
226   m_forwardBuffer.Clear();
227 }
228
229