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