]> git.vomp.tv Git - vompserver.git/blob - recplayer.c
15 years that line of code has been waiting to crash
[vompserver.git] / recplayer.c
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "recplayer.h"
22
23 #ifndef _XOPEN_SOURCE
24 #define _XOPEN_SOURCE 600
25 #endif
26
27 #include <fcntl.h>
28
29 RecPlayer::RecPlayer(const cRecording* rec)
30 {
31   log = Log::getInstance();
32   file = NULL;
33   fileOpen = 0;
34   lastPosition = 0;
35   recording = rec;
36   for(int i = 1; i < 1000; i++) segments[i] = NULL;
37
38   // FIXME find out max file path / name lengths
39 #if VDRVERSNUM < 10703
40   indexFile = new cIndexFile(recording->FileName(), false);
41 #else
42   indexFile = new cIndexFile(recording->FileName(), false,  recording->IsPesRecording());
43 #endif
44   if (!indexFile) log->log("RecPlayer", Log::ERR, "Failed to create indexfile!");
45
46   scan();
47 }
48
49 void RecPlayer::scan()
50 {
51   if (file) fclose(file);
52   totalLength = 0;
53   fileOpen = 0;
54   totalFrames = 0;
55
56   int i = 1;
57   while(segments[i++]) delete segments[i];
58
59   char fileName[2048];
60 #if VDRVERSNUM < 10703
61   for(i = 1; i < 255; i++)//maximum is 255 files instead of 1000, according to VDR HISTORY file...
62   {
63     snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
64 #else
65   for(i = 1; i < 65535; i++)
66   {
67     if (recording->IsPesRecording())
68       snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
69     else
70       snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
71 #endif
72     log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
73     file = fopen(fileName, "r");
74     if (!file) break;
75
76     segments[i] = new Segment();
77     segments[i]->start = totalLength;
78     fseeko(file, 0, SEEK_END);
79     totalLength += ftello(file);
80     totalFrames = indexFile->Last();
81     log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
82     segments[i]->end = totalLength;
83     fclose(file);
84   }
85
86   file = NULL;
87 }
88
89 RecPlayer::~RecPlayer()
90 {
91   log->log("RecPlayer", Log::DEBUG, "destructor");
92   int i = 1;
93   while(segments[i++]) delete segments[i];
94   if (file) fclose(file);
95 }
96
97 int RecPlayer::openFile(int index)
98 {
99   if (file) fclose(file);
100
101   char fileName[2048];
102 #if VDRVERSNUM < 10703
103   snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
104 #else
105   if (recording->IsPesRecording())
106     snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
107   else
108     snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
109 #endif
110   log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
111
112   file = fopen(fileName, "r");
113   if (!file)
114   {
115     log->log("RecPlayer", Log::DEBUG, "file failed to open");
116     fileOpen = 0;
117     return 0;
118   }
119   fileOpen = index;
120   return 1;
121 }
122
123 ULLONG RecPlayer::getLengthBytes()
124 {
125   return totalLength;
126 }
127
128 ULONG RecPlayer::getLengthFrames()
129 {
130   return totalFrames;
131 }
132
133 unsigned long RecPlayer::getBlock(unsigned char* buffer, ULLONG position, unsigned long amount)
134 {
135   if ((amount > totalLength) || (amount > 1000000))
136   {
137     log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
138     return 0;
139   }
140
141   if (position >= totalLength)
142   {
143     log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
144     return 0;
145   }
146
147   if ((position + amount) > totalLength)
148   {
149     log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
150     amount = totalLength - position;
151   }
152
153   // work out what block position is in
154   int segmentNumber;
155   for(segmentNumber = 1; segmentNumber < 1000; segmentNumber++)
156   {
157     if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
158     // position is in this block
159   }
160
161   // we could be seeking around
162   if (segmentNumber != fileOpen)
163   {
164     if (!openFile(segmentNumber)) return 0;
165   }
166
167   ULLONG currentPosition = position;
168   ULONG yetToGet = amount;
169   ULONG got = 0;
170   ULONG getFromThisSegment = 0;
171   ULLONG filePosition;
172
173   while(got < amount)
174   {
175     if (got)
176     {
177       // if(got) then we have already got some and we are back around
178       // advance the file pointer to the next file
179       if (!openFile(++segmentNumber)) return 0;
180     }
181
182     // is the request completely in this block?
183     if ((currentPosition + yetToGet) <= segments[segmentNumber]->end)
184       getFromThisSegment = yetToGet;
185     else
186       getFromThisSegment = segments[segmentNumber]->end - currentPosition;
187
188     filePosition = currentPosition - segments[segmentNumber]->start;
189     fseeko(file, filePosition, SEEK_SET);
190     if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem.
191  
192     // Tell linux not to bother keeping the data in the FS cache
193     posix_fadvise(file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
194  
195     got += getFromThisSegment;
196     currentPosition += getFromThisSegment;
197     yetToGet -= getFromThisSegment;
198   }
199
200   lastPosition = position;
201   return got;
202 }
203
204 ULLONG RecPlayer::getLastPosition()
205 {
206   return lastPosition;
207 }
208
209 const cRecording* RecPlayer::getCurrentRecording()
210 {
211   return recording;
212 }
213
214 ULLONG RecPlayer::positionFromFrameNumber(ULONG frameNumber)
215 {
216   if (!indexFile) return 0;
217 #if VDRVERSNUM < 10703
218   uchar retFileNumber;
219   int retFileOffset;
220   uchar retPicType;
221 #else
222   uint16_t retFileNumber;
223   off_t retFileOffset;
224   bool retPicType;
225 #endif
226   int retLength;
227
228
229   if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset, &retPicType, &retLength))
230   {
231     return 0;
232   }
233
234 //  log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
235   if (!segments[retFileNumber]) return 0;
236   ULLONG position = segments[retFileNumber]->start + retFileOffset;
237 //  log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position);
238
239   return position;
240 }
241
242 ULONG RecPlayer::frameNumberFromPosition(ULLONG position)
243 {
244   if (!indexFile) return 0;
245
246   if (position >= totalLength)
247   {
248     log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
249     return 0;
250   }
251
252   uchar segmentNumber;
253   for(segmentNumber = 1; segmentNumber < 255; segmentNumber++)
254   {
255     if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
256     // position is in this block
257   }
258   ULLONG askposition = position - segments[segmentNumber]->start;
259   return indexFile->Get((int)segmentNumber, askposition);
260
261 }
262
263
264 bool RecPlayer::getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength)
265 {
266   // 0 = backwards
267   // 1 = forwards
268
269   if (!indexFile) return false;
270
271 #if VDRVERSNUM < 10703
272   uchar waste1;
273   int waste2;
274 #else
275   uint16_t waste1;
276   off_t waste2;
277 #endif
278
279   int iframeLength;
280   int indexReturnFrameNumber;
281
282   indexReturnFrameNumber = (ULONG)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), &waste1, &waste2, &iframeLength);
283   log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
284
285   if (indexReturnFrameNumber == -1) return false;
286
287   *rfilePosition = positionFromFrameNumber(indexReturnFrameNumber);
288   *rframeNumber = (ULONG)indexReturnFrameNumber;
289   *rframeLength = (ULONG)iframeLength;
290
291   return true;
292 }