]> git.vomp.tv Git - vompclient.git/blob - demuxervdr.cc
Count frames properly in VDR demuxer
[vompclient.git] / demuxervdr.cc
1 /*
2     Copyright 2005-2006 Mark Calderbank
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "demuxervdr.h"
22 #include "video.h"
23 #ifndef WIN32
24 #include <endian.h>
25 #else
26 #define __LITTLE_ENDIAN 1234
27 #define __BIG_ENDIAN  4321
28 #define __BYTE_ORDER __LITTLE_ENDIAN
29 #endif
30
31 #define PTS_JUMP_MARGIN   10000
32 #define PTS_ALLOWANCE 90000
33
34 // TODO: PTS class to handle wrapping arithmetic & comparisons?
35 static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2)
36 {
37   // Assume pts1, pts2 < 2^33; calculate shortest distance between
38   ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1;
39   if (ret > (1LL<<32)) ret = (1LL<<33) - ret;
40   return ret;
41 }
42
43 static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
44 {
45   // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
46   if (pts1 > pts2)
47     return pts1 - pts2;
48   else
49     return (1LL<<33) + pts1 - pts2;
50 }
51
52 DemuxerVDR::DemuxerVDR()
53 {
54   frameCounting = false;
55 }
56
57 void DemuxerVDR::reset()
58 {
59   frameCounting = false;
60   pts_map.clear();
61   Demuxer::reset();
62 }
63
64 void DemuxerVDR::flush()
65 {
66   state = 0;
67   submitting = false;
68   Demuxer::flush();
69 }
70
71 int DemuxerVDR::scan(UCHAR *buf, int len)
72 {
73   // Temporarily, just look for the lowest audio stream and return it
74   UCHAR HiByte = PESTYPE_AUDMAX;
75   int ret = 0;
76
77   while (len >= 4)
78   {
79     UINT pattern = *(UINT*)buf;
80     buf++; len--;
81
82 #if __BYTE_ORDER == __BIG_ENDIAN
83     if (pattern < (UINT)(0x100 | PESTYPE_AUD0) ||
84         pattern > (UINT)(0x100 | HiByte)) continue;
85     HiByte = pattern & 0xFF;
86 #else
87     if ((pattern & 0xFFFFFF) != 0x010000 ||
88          pattern < ((UINT)PESTYPE_AUD0 << 24) ||
89          pattern > ((UINT)HiByte << 24)) continue;
90     HiByte = pattern >> 24;
91 #endif
92     ret = HiByte;
93     if (HiByte == PESTYPE_AUD0) break;
94   }
95   return ret;
96 }
97
98 int DemuxerVDR::findVideoPTS(UCHAR* buf, int len, ULLONG* dest)
99 {
100   while (len >= 14)
101   {
102     UINT pattern = *(UINT*)buf;
103     buf++; len--;
104 #if __BYTE_ORDER == __BIG_ENDIAN
105     if (pattern < (0x100 | PESTYPE_VID0) ||
106         pattern > (0x100 | PESTYPE_VIDMAX)) continue;
107 #else
108     if ((pattern & 0xFFFFFF) != 0x010000 ||
109          pattern < ((UINT)PESTYPE_VID0 << 24) ||
110          pattern > ((UINT)PESTYPE_VIDMAX << 24)) continue;
111 #endif
112     if ((buf[5] & 0xC0) != 0x80) continue;
113
114     UINT packetlength = ((UINT)buf[3] << 8) | buf[4];
115
116     if ( buf[6] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
117     {
118       if ( (buf[8]  & 0x01) != 0x01 ||
119            (buf[10] & 0x01) != 0x01 ||
120            (buf[12] & 0x01) != 0x01) continue;
121
122       *dest = ( (ULLONG)(buf[8]  & 0x0E) << 29 ) |
123               ( (ULLONG)(buf[9])         << 22 ) |
124               ( (ULLONG)(buf[10] & 0xFE) << 14 ) |
125               ( (ULLONG)(buf[11])        <<  7 ) |
126               ( (ULLONG)(buf[12] & 0xFE) >>  1 );
127       return 1;
128     }
129
130     buf += 5; len -= 5;
131     buf += packetlength; len -= packetlength;
132   }
133   // No PTS found.
134   return 0;
135 }
136
137 void DemuxerVDR::setFrameNum(ULONG frame)
138 {
139   frameCounting = true;
140   frameNumber = frame;
141   Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame);
142 }
143
144 #ifndef NEW_DEMUXER
145 int DemuxerVDR::put(UCHAR* buf, int len)
146 #else
147 int DemuxerVDR::put(UCHAR* buf, int len, ULLONG cur_pos)
148 #endif
149 {
150   int ret = 0;    // return number of bytes consumed
151
152 #ifdef NEW_DEMUXER
153   ULLONG current_position = cur_pos;
154 #endif
155
156   if (submitting)
157   {
158 #ifndef NEW_DEMUXER
159     if (packet.submit() == 0) // Still full!
160 #else
161     if (packet.submit(current_position) == 0) // Still full!
162 #endif
163       return ret;
164     else
165       submitting = false;
166   }
167
168   if (state > 0) // We are half way through a PES packet.
169   {
170     if (len >= state) // The remainder of the packet is available.
171     {
172       packet.write(buf, state);
173       buf += state; len -= state; ret += state;
174       state = 0;
175 #ifndef NEW_DEMUXER
176     if (packet.submit() == 0) // Stream is full
177 #else
178     if (packet.submit(current_position) == 0) // Stream is full
179 #endif
180       {
181         submitting = true;
182         return ret;
183       }
184     }
185     else // Write what we have, then exit.
186     {
187       packet.write(buf, len);
188       state -= len;
189       return len;
190     }
191   }
192
193   while (len > 0)
194   {
195     switch (state)
196     {
197       case  0:
198       case -1:
199         if (*buf == 0x00) state--; else state = 0;
200         buf++; len--; ret++;
201         break;
202       case -2:
203         if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0;
204         buf++; len--; ret++;
205         break;
206       case -3:
207         if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) ||
208             (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) ||
209             (*buf == PESTYPE_PRIVATE_1))
210         {
211           packet.init(*buf);
212           state--;
213         }
214         else if (*buf == 0x00)
215           state = -1;
216         else
217           state = 0;
218         buf++; len--; ret++;
219         break;
220       case -4:
221         packetLength = ((UINT)*buf) << 8;
222         state--;
223         buf++; len--; ret++;
224         break;
225       case -5:
226         packetLength += *buf;
227         state--;
228         buf++; len--; ret++;
229         break;
230     }
231
232 #ifdef NEW_DEMUXER
233     current_position++;
234 #endif
235
236     if (state == -6) // Packet header complete
237     {
238       if (len >= packetLength) // The entire packet is available.
239       {
240         packet.write(buf, packetLength);
241         buf += packetLength; len -= packetLength; ret += packetLength;
242 #ifdef NEW_DEMUXER
243         current_position+=(ULLONG)packetLength;
244 #endif
245         state = 0;
246 #ifndef NEW_DEMUXER
247         if (packet.submit() == 0) // Stream is full
248 #else
249         if (packet.submit(current_position) == 0) // Stream is full
250 #endif
251         {
252           submitting = true;
253           return ret;
254         }
255       }
256       else // Write what we have.
257       {
258         packet.write(buf, len);
259         state = packetLength - len;
260         ret += len;
261         len = 0;
262       }
263     }
264   }
265   return ret;
266 }
267
268 ULONG DemuxerVDR::getFrameNumFromPTS(ULLONG pts)
269 {
270   ULLONG difference = (1LL<<33);
271   ULONG ref_frame = 0;
272   int total = 0, actual = 0;
273   pthread_mutex_lock(&pts_map_mutex);
274   PTSMap::iterator iter = pts_map.begin();
275   while (iter != pts_map.end())
276   {
277     ++total;
278     if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE)
279     {
280       difference = 0;
281       ref_frame = iter->frame;
282       actual = total;
283       break;
284     }
285     ULLONG newdiff = PTSDifference(pts, iter->pts);
286     if (newdiff < difference)
287     {
288       difference = newdiff;
289       ref_frame = iter->frame;
290       actual = total;
291     }
292     ++iter;
293   }
294   if (total > 1 && actual == 1) // We are using the most recent PTS ref.
295   {                             // Delete the rest.
296     iter = pts_map.begin(); iter++;
297     pts_map.erase(iter, pts_map.end());
298   }
299   pthread_mutex_unlock(&pts_map_mutex);
300   if (total > 1 && actual == 1)
301 Log::getInstance()->log("Demuxer", Log::DEBUG, "DELETED REFERENCES");
302   if (actual > 1)
303 Log::getInstance()->log("Demuxer", Log::DEBUG, "STILL USING OLD REF");
304
305 //ULONG ret = ref_frame + difference * Video::getInstance()->getFPS() / 90000;
306 //Log::getInstance()->log("Demuxer", Log::DEBUG, "%llu: FOUND %d from %d, diff %llu", pts, ret, ref_frame, difference);
307
308   if (difference == (1LL<<33))
309     return 0; // We cannot make sense of the pts
310   else
311     return ref_frame + difference * Video::getInstance()->getFPS() / 90000;
312 }
313
314 void DemuxerVDR::PESPacketVDR::parseDetails()
315 {
316   DemuxerVDR* dx = (DemuxerVDR*)(DemuxerVDR::getInstance());
317   PESPacket::parseDetails();
318
319   if (dx->frameCounting && findPictureHeader() &&
320       packetType >= PESTYPE_VID0 && packetType <= PESTYPE_VIDMAX)
321   {
322     ULONG frame_num = (dx->frameNumber)++;
323     if (seq_header && pts != PTS_INVALID)
324     {
325       PTSMapEntry me;
326       pthread_mutex_lock(&(dx->pts_map_mutex));
327       if (dx->pts_map.empty())
328       {
329         me.pts = pts;
330         me.frame = frame_num;
331         pthread_mutex_unlock(&(dx->pts_map_mutex));
332 Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS INIT *+ %llu %u", me.pts, me.frame);
333         pthread_mutex_lock(&(dx->pts_map_mutex));
334         dx->pts_map.push_front(me);
335       }
336       me = dx->pts_map.front();
337       pthread_mutex_unlock(&(dx->pts_map_mutex));
338
339       UINT fps = Video::getInstance()->getFPS();
340       ULLONG pts_expected = me.pts + 90000*(frame_num - me.frame) / fps;
341       while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33);
342
343       if (PTSDistance(pts_expected, pts) > PTS_JUMP_MARGIN) // PTS jump!
344       {
345 Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS JUMP *+ %llu %u", pts, frame_num);
346         me.pts = pts;
347         me.frame = frame_num;
348         pthread_mutex_lock(&(dx->pts_map_mutex));
349         dx->pts_map.push_front(me);
350         pthread_mutex_unlock(&(dx->pts_map_mutex));
351       }
352     }
353   }
354 }