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