]> git.vomp.tv Git - vompclient.git/blob - demuxermedia.cc
Display channel name, duration, resume point and size on recording info screen
[vompclient.git] / demuxermedia.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 "demuxermedia.h"
22 #include "callback.h"
23
24 #include "video.h"
25 #include "log.h"
26
27 #ifndef WIN32
28 #include <endian.h>
29 #else
30 #define __LITTLE_ENDIAN 1234
31 #define __BIG_ENDIAN  4321
32 #define __BYTE_ORDER __LITTLE_ENDIAN
33 #endif
34
35 #define PTS_JUMP_MARGIN   10000
36 #define PTS_ALLOWANCE 90000
37
38 // TODO: PTS class to handle wrapping arithmetic & comparisons?
39 static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2)
40 {
41   // Assume pts1, pts2 < 2^33; calculate shortest distance between
42   ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1;
43   if (ret > (1LL<<32)) ret = (1LL<<33) - ret;
44   return ret;
45 }
46
47 DemuxerMedia::DemuxerMedia()
48 {
49   frameCounting = false;
50   packetCounting = false;
51 }
52
53 void DemuxerMedia::reset()
54 {
55   frameCounting = false;
56   packetCounting = false;
57   pts_map.clear();
58   Demuxer::reset();
59   firstPTS=0;
60   lastPTS=0;
61   currentPTS=0;
62   last_horizontal_size=-1;
63   last_vertical_size=-1;
64 }
65
66 void DemuxerMedia::flush()
67 {
68   state = 0;
69   submitting = false;
70   Demuxer::flush();
71 }
72
73 int DemuxerMedia::scan(UCHAR *buf, int len)
74 {
75   int ret = 0;
76   int astate=0; //hdrbyte
77
78   while ((len + astate)> 3)
79   {
80     switch (astate) {
81       case 0:  //1st hdr byte
82         if (*buf != 0) break;
83         astate++;
84         break;
85       case 1:  //2nd hdrbyte
86         if (*buf != 0) {
87           astate=0;
88           break;
89         }
90         astate++;
91         break;
92       case 2: //3rd header byte
93         if (*buf != 1) {
94           astate=0;
95           break;
96         }
97         astate++;
98         break;
99       case 3: //4th header byte
100         if ((*buf <PESTYPE_AUD0 || *buf > PESTYPE_AUDMAX) && *buf != PESTYPE_PRIVATE_1) {
101           astate=0;
102           break;
103         }
104         if (*buf != PESTYPE_PRIVATE_1) {
105           //found audio
106           ret=*buf;
107           return ret;
108         }
109         //AC3 - TODO
110         astate=0;
111       default:
112         astate=0;
113         break;
114     }
115     buf++;
116     len--;
117   }
118   return ret;
119 }
120
121 int DemuxerMedia::findPTS(UCHAR* buf, int len, ULLONG* dest)
122 {
123   // nobody uses this
124   // No PTS found.
125   return 0;
126 }
127
128 void DemuxerMedia::setFrameNum(ULONG frame)
129 {
130   frameCounting = true;
131   frameNumber = frame;
132   Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame);
133 }
134
135 void DemuxerMedia::setPacketNum(ULONG npacket)
136 {
137   packetCounting = true;
138   packetNumber = npacket;
139   Log::getInstance()->log("Demuxer", Log::DEBUG, "setPacketNum %d", npacket);
140 }
141
142 int DemuxerMedia::put(UCHAR* buf, int len)
143 {
144   int ret = 0;    // return number of bytes consumed
145   if (submitting)
146   {
147     if (submitPacket(packet) == 0) // Still full!
148       return ret;
149     else
150       submitting = false;
151   }
152
153   if (state > 0) // We are half way through a PES packet.
154   {
155     if (len >= state) // The remainder of the packet is available.
156     {
157       packet.write(buf, state);
158       buf += state; len -= state; ret += state;
159       state = 0;
160       parseVDRPacketDetails();
161       if (submitPacket(packet) == 0) // Stream is full
162       {
163         submitting = true;
164         return ret;
165       }
166     }
167     else // Write what we have, then exit.
168     {
169       packet.write(buf, len);
170       state -= len;
171       return len;
172     }
173   }
174
175   while (len > 0)
176   {
177     switch (state)
178     {
179       case  0:
180       case -1:
181         if (*buf == 0x00) state--; else state = 0;
182         buf++; len--; ret++;
183         break;
184       case -2:
185         if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0;
186         buf++; len--; ret++;
187         break;
188       case -3:
189         if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) ||
190             (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) ||
191             (*buf == PESTYPE_PRIVATE_1))
192         {
193           packet.init(*buf);
194           state--;
195         }
196         else if (*buf == 0x00)
197           state = -1;
198         else
199           state = 0;
200         buf++; len--; ret++;
201         break;
202       case -4:
203         packetLength = ((UINT)*buf) << 8;
204         state--;
205         buf++; len--; ret++;
206         break;
207       case -5:
208         packetLength += *buf;
209         state--;
210         buf++; len--; ret++;
211         break;
212     }
213
214     if (state == -6) // Packet header complete
215     {
216       if (len >= packetLength) // The entire packet is available.
217       {
218         packet.write(buf, packetLength);
219         buf += packetLength; len -= packetLength; ret += packetLength;
220         state = 0;
221         parseVDRPacketDetails();
222         if (submitPacket(packet) == 0) // Stream is full
223         {
224           submitting = true;
225           return ret;
226         }
227       }
228       else // Write what we have.
229       {
230         packet.write(buf, len);
231         state = packetLength - len;
232         ret += len;
233         len = 0;
234       }
235     }
236   }
237   return ret;
238 }
239
240 ULONG DemuxerMedia::getPacketNum()
241 {
242   return packetNumber;
243 }
244
245 void DemuxerMedia::parseVDRPacketDetails()
246 {
247   parsePacketDetails(packet);
248
249   if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 &&
250                         packet.getPacketType() <= PESTYPE_AUDMAX)
251   {
252     packetNumber++;
253   }
254
255   if (frameCounting && packet.findPictureHeader(h264) &&
256       packet.getPacketType() >= PESTYPE_VID0 &&
257       packet.getPacketType() <= PESTYPE_VIDMAX)
258   {
259     ULLONG pts=packet.getPTS();
260     if (packet.findSeqHeader(h264) > 1 && pts != PESPacket::PTS_INVALID)
261     {
262       if (firstPTS == 0) firstPTS=pts;
263       currentPTS=pts;
264
265     }
266   }
267   if (packet.getPacketType() >= PESTYPE_VID0 &&
268       packet.getPacketType() <= PESTYPE_VIDMAX &&
269       packet.findSeqHeader(h264)) {
270     //check video size
271     if (horizontal_size != last_horizontal_size ||
272         vertical_size != last_vertical_size) {
273       last_horizontal_size=horizontal_size;
274       last_vertical_size=vertical_size;
275       Log::getInstance()->log("Demux", Log::DEBUG,"signal size change, new x %d, y %d",
276           horizontal_size,vertical_size);
277       if (callback) callback->call(this);
278     }
279   }
280       
281 }
282
283 //find a sequence header backward
284 //search for 00 00 01 <video> backwards
285 ULLONG DemuxerMedia::findLastPTS(UCHAR *buffer, ULONG len) {
286   PESPacket pack;
287   int pstate=4;
288   ULONG minlen=12;
289   if (len < minlen) return 0;
290   UCHAR *curpos=buffer+len-minlen;
291   UCHAR *packend=buffer+len;
292   while ((curpos - pstate) > buffer) {
293     switch (pstate){
294       case 4:
295         if (*curpos < PESTYPE_VID0 || *curpos > PESTYPE_VIDMAX) break;
296         pstate--;
297         break;
298       case 3:
299         if (*curpos != 0x1) {
300           pstate=4;
301           continue;
302         }
303         pstate--;
304         break;
305       case 2:
306       case 1:
307         if (*curpos != 0) {
308           pstate=4;
309           continue;
310         }
311         pstate--;
312         break;
313       default:
314         pstate=4;
315     }
316     curpos--;
317     if (pstate != 0) {
318       continue;
319     }
320     //we found a header
321     //curpos points before the first 0
322     pack.init(*(curpos+4));
323     pack.write(curpos+7,packend-curpos-7);
324     packend=curpos+1;
325     ULLONG pts=pack.getPTS();
326     if (pts != PESPacket::PTS_INVALID) {
327       //ok we have it
328       lastPTS=pts;
329       Log::getInstance()->log("DemuxerMedia",Log::DEBUG,"findLastPTS found pts %llu at packet offset %lu from end",pts,len-(curpos+1-buffer));
330       return pts;
331     }
332     //hmm - still no PTS, continue search
333     state=4;
334   }
335   return 0;
336 }
337
338 ULLONG DemuxerMedia::getCurrentPTS() {
339   return PTSDistance(currentPTS,firstPTS);
340 }
341 ULLONG DemuxerMedia::getLenPTS() {
342   if (lastPTS == 0) return 0;
343   if (lastPTS < firstPTS) return 0;
344   return PTSDistance(lastPTS,firstPTS);
345 }
346
347
348