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