enable playback of multifile (split) movies
[enigma2.git] / lib / dvb / tstools.cpp
1 #include <lib/dvb/tstools.h>
2 #include <lib/base/eerror.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5
6 #include <stdio.h>
7
8 eDVBTSTools::eDVBTSTools()
9 {
10         m_pid = -1;
11         m_maxrange = 256*1024;
12         
13         m_begin_valid = 0;
14         m_end_valid = 0;
15         
16         m_use_streaminfo = 0;
17 }
18
19 eDVBTSTools::~eDVBTSTools()
20 {
21         closeFile();
22 }
23
24 int eDVBTSTools::openFile(const char *filename)
25 {
26         closeFile();
27         
28         m_streaminfo.load((std::string(filename) + ".ap").c_str());
29         
30         if (!m_streaminfo.empty())
31                 m_use_streaminfo = 1;
32         else
33         {
34                 eDebug("no recorded stream information available");
35                 m_use_streaminfo = 0;
36         }
37
38         if (m_file.open(filename) < 0)
39                 return -1;
40         return 0;
41 }
42
43 void eDVBTSTools::closeFile()
44 {
45         m_file.close();
46 }
47
48 void eDVBTSTools::setSyncPID(int pid)
49 {
50         m_pid = pid;
51 }
52
53 void eDVBTSTools::setSearchRange(int maxrange)
54 {
55         m_maxrange = maxrange;
56 }
57
58         /* getPTS extracts a pts value from any PID at a given offset. */
59 int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
60 {
61         if (m_use_streaminfo)
62                 return m_streaminfo.getPTS(offset, pts);
63         
64         if (!m_file.valid() < 0)
65                 return -1;
66
67         offset -= offset % 188;
68         
69         if (m_file.lseek(offset, SEEK_SET) < 0)
70                 return -1;
71
72         int left = m_maxrange;
73         
74         while (left >= 188)
75         {
76                 unsigned char block[188];
77                 if (m_file.read(block, 188) != 188)
78                 {
79                         eDebug("read error");
80                         break;
81                 }
82                 left -= 188;
83                 offset += 188;
84                 
85                 if (block[0] != 0x47)
86                 {
87                         int i = 0;
88                         while (i < 188)
89                         {
90                                 if (block[i] == 0x47)
91                                         break;
92                                 ++i;
93                         }
94                         offset = m_file.lseek(i - 188, SEEK_CUR);
95                         continue;
96                 }
97                 
98                 int pid = ((block[1] << 8) | block[2]) & 0x1FFF;
99                 int pusi = !!(block[1] & 0x40);
100                 
101 //              printf("PID %04x, PUSI %d\n", pid, pusi);
102                 
103                 if (m_pid >= 0)
104                         if (pid != m_pid)
105                                 continue;
106                 if (!pusi)
107                         continue;
108                 
109                         /* ok, now we have a PES header */
110                 unsigned char *pes;
111                 
112                         /* check for adaption field */
113                 if (block[3] & 0x20)
114                         pes = block + block[4] + 4 + 1;
115                 else
116                         pes = block + 4;
117                 
118                         /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
119                 if (pes[0] || pes[1] || (pes[2] != 1))
120                         continue;
121                 
122                 if (pes[7] & 0x80) /* PTS */
123                 {
124                         pts  = ((unsigned long long)(pes[ 9]&0xE))  << 29;
125                         pts |= ((unsigned long long)(pes[10]&0xFF)) << 22;
126                         pts |= ((unsigned long long)(pes[11]&0xFE)) << 14;
127                         pts |= ((unsigned long long)(pes[12]&0xFF)) << 7;
128                         pts |= ((unsigned long long)(pes[13]&0xFE)) >> 1;
129                         offset -= 188;
130                         
131                                 /* convert to zero-based */
132                         if (fixed)
133                                 fixupPTS(offset, pts);
134                         return 0;
135                 }
136         }
137         
138         return -1;
139 }
140
141 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
142 {
143         if (m_use_streaminfo)
144         {
145                 return m_streaminfo.fixupPTS(offset, now);
146         } else
147         {
148                         /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
149                 calcBegin();
150                 if (!m_begin_valid)
151                 {       
152                         eDebug("begin not valid, can't fixup");
153                         return -1;
154                 }
155                 
156                 pts_t pos = m_pts_begin;
157                 if ((now < pos) && ((pos - now) < 90000 * 10))
158                 {       
159                         pos = 0;
160                         return 0;
161                 }
162                 
163                 if (now < pos) /* wrap around */
164                         now = now + 0x200000000LL - pos;
165                 else
166                         now -= pos;
167                 return 0;
168         }
169 }
170
171 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts)
172 {
173         if (m_use_streaminfo)
174         {
175                 offset = m_streaminfo.getAccessPoint(pts);
176                 return 0;
177         } else
178         {
179                 int bitrate = calcBitrate(); /* in bits/s */
180                 if (bitrate <= 0)
181                         return -1;
182                 
183                 offset = (pts * (pts_t)bitrate) / 8ULL / 90000ULL;
184                 offset -= offset % 188;
185
186                 return 0;
187         }
188 }
189
190 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
191 {
192         if (m_use_streaminfo)
193                 return m_streaminfo.getNextAccessPoint(ts, start, direction);
194         else
195         {
196                 eDebug("can't get next access point without streaminfo");
197                 return -1;
198         }
199 }
200
201 void eDVBTSTools::calcBegin()
202 {
203         if (!m_file.valid())
204                 return;
205
206         if (!m_begin_valid)
207         {
208                 m_offset_begin = 0;
209                 if (!getPTS(m_offset_begin, m_pts_begin))
210                         m_begin_valid = 1;
211         }
212 }
213
214 void eDVBTSTools::calcEnd()
215 {
216         if (!m_file.valid())
217                 return;
218         
219         off_t end = m_file.lseek(0, SEEK_END);
220         
221         if (abs(end - m_offset_end) > 1*1024*1024)
222         {
223                 m_offset_end = end;
224                 m_end_valid = 0;
225                 eDebug("file size changed, recalc length");
226         }
227         
228         int maxiter = 10;
229         
230         while (!m_end_valid)
231         {
232                 if (!--maxiter)
233                         return;
234                 
235                 m_offset_end -= m_maxrange;
236                 if (m_offset_end < 0)
237                         m_offset_end = 0;
238                 if (!getPTS(m_offset_end, m_pts_end))
239                         m_end_valid = 1;
240                 if (!m_offset_end)
241                         return;
242         }
243 }
244
245 int eDVBTSTools::calcLen(pts_t &len)
246 {
247         calcBegin(); calcEnd();
248         if (!(m_begin_valid && m_end_valid))
249                 return -1;
250         len = m_pts_end - m_pts_begin;
251                 /* wrap around? */
252         if (len < 0)
253                 len += 0x200000000LL;
254         return 0;
255 }
256
257 int eDVBTSTools::calcBitrate()
258 {
259         calcBegin(); calcEnd();
260         if (!(m_begin_valid && m_end_valid))
261                 return -1;
262
263         pts_t len_in_pts = m_pts_end - m_pts_begin;
264
265                 /* wrap around? */
266         if (len_in_pts < 0)
267                 len_in_pts += 0x200000000LL;
268         off_t len_in_bytes = m_offset_end - m_offset_begin;
269         
270         if (!len_in_pts)
271                 return -1;
272         
273         unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
274         if ((bitrate < 10000) || (bitrate > 100000000))
275                 return -1;
276         
277         return bitrate;
278 }