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