use cached pts/offsets when available, clean up
[vuplus_dvbapp] / 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                 m_use_streaminfo = 0;
35
36         m_fd = ::open(filename, O_RDONLY);
37         if (m_fd < 0)
38                 return -1;
39         return 0;
40 }
41
42 void eDVBTSTools::closeFile()
43 {
44         if (m_fd >= 0)
45                 ::close(m_fd);
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)
60 {
61         if (m_use_streaminfo)
62         {
63                 off_t off = offset;
64                 pts_t p = pts;
65                 int r = m_streaminfo.getPTS(off, p);
66                 eDebug("streaminfo result: %d, %08llx, %08llx", r, off, p);
67         }
68         
69         if (m_fd < 0)
70                 return -1;
71
72         offset -= offset % 188;
73         
74                 // TODO: multiple files!        
75         if (lseek(m_fd, offset, SEEK_SET) < 0)
76                 return -1;
77
78         int left = m_maxrange;
79         
80         while (left >= 188)
81         {
82                 unsigned char block[188];
83                 if (read(m_fd, block, 188) != 188)
84                         break;
85                 left -= 188;
86                 offset += 188;
87                 
88                 if (block[0] != 0x47)
89                 {
90                         int i = 0;
91                         while (i < 188)
92                         {
93                                 if (block[i] == 0x47)
94                                         break;
95                                 ++i;
96                         }
97                         offset = lseek(m_fd, i - 188, SEEK_CUR);
98                         continue;
99                 }
100                 
101                 int pid = ((block[1] << 8) | block[2]) & 0x1FFF;
102                 int pusi = !!(block[1] & 0x40);
103                 
104 //              printf("PID %04x, PUSI %d\n", pid, pusi);
105                 
106                 if (m_pid >= 0)
107                         if (pid != m_pid)
108                                 continue;
109                 if (!pusi)
110                         continue;
111                 
112                         /* ok, now we have a PES header */
113                 unsigned char *pes;
114                 
115                         /* check for adaption field */
116                 if (block[3] & 0x20)
117                         pes = block + block[4] + 4 + 1;
118                 else
119                         pes = block + 4;
120                 
121                         /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
122                 if (pes[0] || pes[1] || (pes[2] != 1))
123                         continue;
124                 
125                 if (pes[7] & 0x80) /* PTS */
126                 {
127                         pts  = ((unsigned long long)(pes[ 9]&0xE))  << 29;
128                         pts |= ((unsigned long long)(pes[10]&0xFF)) << 22;
129                         pts |= ((unsigned long long)(pes[11]&0xFE)) << 14;
130                         pts |= ((unsigned long long)(pes[12]&0xFF)) << 7;
131                         pts |= ((unsigned long long)(pes[13]&0xFE)) >> 1;
132                         offset -= 188;
133                         
134                                 /* convert to zero-based */
135                         fixupPTS(offset, pts);
136                         eDebug("tstools result: %08llx, %08llx", offset, pts);
137                         return 0;
138                 }
139         }
140         
141         return -1;
142 }
143
144 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
145 {
146         if (m_use_streaminfo)
147         {
148                 eDebug("streaminfo fixup, before %08llx", now);
149                 int r = m_streaminfo.fixupPTS(offset, now);
150                 eDebug("streaminfo fixup, after  %08llx", now);
151                 return r;
152         } else
153         {
154                         /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
155                 calcBegin();
156                 off_t begin = 0;
157                 pts_t pos;
158                 if (getPTS(begin, pos))
159                         return -1;
160                 if ((now < pos) && ((pos - now) < 90000 * 10))
161                 {
162                         pos = 0;
163                         return 0;
164                 }
165                 if (now < pos) /* wrap around */
166                         pos = now + 0x200000000LL - pos;
167                 else
168                         pos = now - pos;
169                 return 0;
170         }
171 }
172
173 void eDVBTSTools::calcBegin()
174 {
175         if (m_fd < 0)   
176                 return;
177
178         if (!m_begin_valid)
179         {
180                 m_offset_begin = 0;
181                 if (!getPTS(m_offset_begin, m_pts_begin))
182                         m_begin_valid = 1;
183         }
184 }
185
186 void eDVBTSTools::calcEnd()
187 {
188         if (m_fd < 0)   
189                 return;
190         
191         off_t end = lseek(m_fd, 0, SEEK_END);
192         
193         if (abs(end - m_offset_end) > 1*1024*1024)
194         {
195                 m_offset_end = end;
196                 m_end_valid = 0;
197                 eDebug("file size changed, recalc length");
198         }
199         
200         int maxiter = 10;
201         
202         while (!m_end_valid)
203         {
204                 if (!--maxiter)
205                         return;
206                 
207                 m_offset_end -= m_maxrange;
208                 if (m_offset_end < 0)
209                         m_offset_end = 0;
210                 if (!getPTS(m_offset_end, m_pts_end))
211                         m_end_valid = 1;
212                 if (!m_offset_end)
213                         return;
214         }
215 }
216
217 int eDVBTSTools::calcLen(pts_t &len)
218 {
219         calcBegin(); calcEnd();
220         if (!(m_begin_valid && m_end_valid))
221                 return -1;
222         len = m_pts_end - m_pts_begin;
223                 /* wrap around? */
224         if (len < 0)
225                 len += 0x200000000LL;
226         return 0;
227 }
228
229 int eDVBTSTools::calcBitrate()
230 {
231         calcBegin(); calcEnd();
232         if (!(m_begin_valid && m_end_valid))
233                 return -1;
234
235         pts_t len_in_pts = m_pts_end - m_pts_begin;
236
237                 /* wrap around? */
238         if (len_in_pts < 0)
239                 len_in_pts += 0x200000000LL;
240         off_t len_in_bytes = m_offset_end - m_offset_begin;
241         
242         if (!len_in_pts)
243                 return -1;
244         
245         unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
246         if ((bitrate < 10000) || (bitrate > 100000000))
247                 return -1;
248         
249         return bitrate;
250 }