]> git.vomp.tv Git - vompclient.git/blob - demuxervdr.cc
*** empty log message ***
[vompclient.git] / demuxervdr.cc
1 /*
2     Copyright 2005-2008 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 Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "demuxervdr.h"
22 #include "video.h"
23 #include "dvbsubtitles.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   subtitlePacketPosition = 0;
60 }
61
62 void DemuxerVDR::reset()
63 {
64   frameCounting = false;
65   packetCounting = false;
66   pts_map.clear();
67   Demuxer::reset();
68 }
69
70 void DemuxerVDR::flush()
71 {
72   state = 0;
73   submitting = false;
74   subtitlePacketPosition = 0;
75   Demuxer::flush();
76 }
77
78 int DemuxerVDR::scan(UCHAR *buf, int len)
79 {
80   // Temporarily, just look for the lowest audio stream and return it
81   UCHAR HiByte = PESTYPE_AUDMAX;
82   int ret = 0;
83
84   while (len >= 4)
85   {
86     UINT pattern = *(UINT*)buf;
87     buf++; len--;
88
89 #if __BYTE_ORDER == __BIG_ENDIAN
90     if (pattern < (UINT)(0x100 | PESTYPE_AUD0) ||
91         pattern > (UINT)(0x100 | HiByte)) continue;
92     HiByte = pattern & 0xFF;
93 #else
94     if ((pattern & 0xFFFFFF) != 0x010000 ||
95          pattern < ((UINT)PESTYPE_AUD0 << 24) ||
96          pattern > ((UINT)HiByte << 24)) continue;
97     HiByte = pattern >> 24;
98 #endif
99     ret = HiByte;
100     if (HiByte == PESTYPE_AUD0) break;
101   }
102   return ret;
103 }
104
105 int DemuxerVDR::findPTS(UCHAR* buf, int len, ULLONG* dest)
106 {
107   while (len >= 14)
108   {
109     UINT pattern = *(UINT*)buf;
110     buf++; len--;
111 #if __BYTE_ORDER == __BIG_ENDIAN
112     if (pattern < (0x100 | PESTYPE_AUD0) ||
113         pattern > (0x100 | PESTYPE_VIDMAX)) continue;
114 #else
115     if ((pattern & 0xFFFFFF) != 0x010000 ||
116          pattern < ((UINT)PESTYPE_AUD0 << 24) ||
117          pattern > ((UINT)PESTYPE_VIDMAX << 24)) continue;
118 #endif
119     if ((buf[5] & 0xC0) != 0x80) continue;
120
121     UINT packetlength = ((UINT)buf[3] << 8) | buf[4];
122
123     if ( buf[6] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
124     {
125       if ( (buf[8]  & 0x01) != 0x01 ||
126            (buf[10] & 0x01) != 0x01 ||
127            (buf[12] & 0x01) != 0x01) continue;
128
129       *dest = ( (ULLONG)(buf[8]  & 0x0E) << 29 ) |
130               ( (ULLONG)(buf[9])         << 22 ) |
131               ( (ULLONG)(buf[10] & 0xFE) << 14 ) |
132               ( (ULLONG)(buf[11])        <<  7 ) |
133               ( (ULLONG)(buf[12] & 0xFE) >>  1 );
134       return 1;
135     }
136
137     buf += 5; len -= 5;
138     buf += packetlength; len -= packetlength;
139   }
140   // No PTS found.
141   return 0;
142 }
143
144 void DemuxerVDR::setFrameNum(ULONG frame)
145 {
146   frameCounting = true;
147   frameNumber = frame;
148   Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame);
149 }
150
151 void DemuxerVDR::setPacketNum(ULONG npacket)
152 {
153   packetCounting = true;
154   packetNumber = npacket;
155   Log::getInstance()->log("Demuxer", Log::DEBUG, "setPacketNum %d", npacket);
156 }
157
158 int DemuxerVDR::put(UCHAR* buf, int len)
159 {
160   int ret = 0;    // return number of bytes consumed
161   if (submitting)
162   {
163     if (submitPacket(packet) == 0) // Still full!
164       return ret;
165     else
166       submitting = false;
167   }
168
169   if (state > 0) // We are half way through a PES packet.
170   {
171     if (len >= state) // The remainder of the packet is available.
172     {
173       packet.write(buf, state);
174       buf += state; len -= state; ret += state;
175       state = 0;
176       parseVDRPacketDetails();
177       if (submitPacket(packet) == 0) // Stream is full
178       {
179         submitting = true;
180         return ret;
181       }
182     }
183     else // Write what we have, then exit.
184     {
185       packet.write(buf, len);
186       state -= len;
187       return len;
188     }
189   }
190
191   while (len > 0)
192   {
193     switch (state)
194     {
195       case  0:
196       case -1:
197         if (*buf == 0x00) state--; else state = 0;
198         buf++; len--; ret++;
199         break;
200       case -2:
201         if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0;
202         buf++; len--; ret++;
203         break;
204       case -3:
205         if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) ||
206             (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) ||
207             (*buf == PESTYPE_PRIVATE_1))
208         {
209           packet.init(*buf);
210           state--;
211         }
212         else if (*buf == 0x00)
213           state = -1;
214         else
215           state = 0;
216         buf++; len--; ret++;
217         break;
218       case -4:
219         packetLength = ((UINT)*buf) << 8;
220         state--;
221         buf++; len--; ret++;
222         break;
223       case -5:
224         packetLength += *buf;
225         state--;
226         buf++; len--; ret++;
227         break;
228     }
229
230     if (state == -6) // Packet header complete
231     {
232       if (len >= packetLength) // The entire packet is available.
233       {
234         packet.write(buf, packetLength);
235         buf += packetLength; len -= packetLength; ret += packetLength;
236         state = 0;
237         parseVDRPacketDetails();
238         if (submitPacket(packet) == 0) // Stream is full
239         {
240           submitting = true;
241           return ret;
242         }
243       }
244       else // Write what we have.
245       {
246         packet.write(buf, len);
247         state = packetLength - len;
248         ret += len;
249         len = 0;
250       }
251     }
252   }
253   return ret;
254 }
255
256 ULONG DemuxerVDR::getPacketNum()
257 {
258   return packetNumber;
259 }
260
261 ULONG DemuxerVDR::getFrameNumFromPTS(ULLONG pts)
262 {
263   ULLONG difference = (1LL<<33);
264   ULONG ref_frame = 0;
265   int total = 0, actual = 0;
266   pts_map_mutex.Lock();
267   PTSMap::iterator iter = pts_map.begin();
268   while (iter != pts_map.end())
269   {
270     ++total;
271     if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE)
272     {
273       difference = 0;
274       ref_frame = iter->frame;
275       actual = total;
276       break;
277     }
278     ULLONG newdiff = PTSDifference(pts, iter->pts);
279     if (newdiff < difference)
280     {
281       difference = newdiff;
282       ref_frame = iter->frame;
283       actual = total;
284     }
285     ++iter;
286   }
287   if (total > 1 && actual == 1) // We are using the most recent PTS ref.
288   {                             // Delete the rest.
289     iter = pts_map.begin(); iter++;
290     pts_map.erase(iter, pts_map.end());
291   }
292   pts_map_mutex.Unlock();
293
294   if (difference == (1LL<<33))
295     return 0; // We cannot make sense of the pts
296   else
297     return ref_frame + difference * Video::getInstance()->getFPS() / 90000;
298 }
299
300 void DemuxerVDR::dealWithSubtitlePacket()
301 {
302   const UCHAR* data = packet.getData();
303   UINT packetSize = packet.getSize();
304   if (packetSize < 9) return;
305   UINT payloadOffset = data[8] + 9;
306   if (packetSize < payloadOffset + 5) return;
307   if (data[payloadOffset + 3] == 0x00)
308   { // Not a continuation packet
309     if (packetSize < payloadOffset + 7) return;
310     subtitlePacket.init(data[3]);
311     subtitlePacket.write(&data[6], payloadOffset - 6);
312     subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4);
313     subtitlePacketPosition = payloadOffset + 2;
314   }
315   else
316   { // Continuation packet
317     if (subtitlePacketPosition == 0) return;
318     subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4);
319   }
320   
321   const UCHAR* sub_data = subtitlePacket.getData();
322   UINT subSize = subtitlePacket.getSize();
323   while (subtitlePacketPosition < subSize)
324   {
325     if (sub_data[subtitlePacketPosition] == 0xFF)
326     { // Packet is complete. Switch it into the "real" packet to be submitted.
327       packet = subtitlePacket;
328       parsePacketDetails(packet);
329       subtitlePacketPosition = 0; // Wait for next non-continuation packet
330       break;
331     }
332     if (sub_data[subtitlePacketPosition] != 0x0F)
333     {
334       subtitlePacketPosition = 0; // Wait for next non-continuation packet
335       break;
336     }
337     if (subSize < subtitlePacketPosition + 6) break;
338     UINT segmentLength = (sub_data[subtitlePacketPosition + 4] << 8)
339                         + sub_data[subtitlePacketPosition + 5];
340     subtitlePacketPosition += segmentLength + 6;
341   }
342 }
343
344 void DemuxerVDR::parseVDRPacketDetails()
345 {
346   if (packet.getPacketType() == PESTYPE_PRIVATE_1 && packet.getSize() > 8)
347   {
348     UINT payload_begin = packet[8] + 9;
349     if (packet.getSize() > payload_begin)
350     {
351       UCHAR substream_type = packet[payload_begin] & 0xF0;
352       if (substream_type == 0x20) // Subtitles
353       {
354         dealWithSubtitlePacket();
355       }
356       else // Not subtitles
357         parsePacketDetails(packet);
358     }
359   }
360   else // Not a private packet*/
361     parsePacketDetails(packet);
362
363   if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 &&
364                         packet.getPacketType() <= PESTYPE_AUDMAX)
365   {
366     packetNumber++;
367   }
368
369   if (frameCounting && packet.findPictureHeader() &&
370       packet.getPacketType() >= PESTYPE_VID0 &&
371       packet.getPacketType() <= PESTYPE_VIDMAX)
372   {
373     ULONG frame_num = (frameNumber)++;
374     if (packet.findSeqHeader() > 1 && packet.hasPTS())
375     {
376       PTSMapEntry me;
377       pts_map_mutex.Lock();
378       if (pts_map.empty())
379       {
380         me.pts = packet.getPTS();
381         me.frame = frame_num;
382         pts_map_mutex.Unlock();
383 Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS INIT *+ %llu %u", me.pts, me.frame);
384         pts_map_mutex.Lock();
385         pts_map.push_front(me);
386       }
387       me = pts_map.front();
388       pts_map_mutex.Unlock();
389
390       UINT fps = Video::getInstance()->getFPS();
391       ULLONG pts_expected = me.pts + 90000*(frame_num - me.frame) / fps;
392       while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33);
393
394       if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump!
395       {
396 Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS JUMP *+ %llu %u", packet.getPTS(), frame_num);
397         me.pts = packet.getPTS();
398         me.frame = frame_num;
399         pts_map_mutex.Lock();
400         pts_map.push_front(me);
401         pts_map_mutex.Unlock();
402       }
403     }
404   }
405 }