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