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