]> git.vomp.tv Git - vompclient.git/blob - demuxer.cc
New PTS-to-frame mapping method in demuxer
[vompclient.git] / demuxer.cc
1 /*
2     Copyright 2005-2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "demuxer.h"
22 #ifndef WIN32
23 #include <endian.h>
24 #else
25 #define __LITTLE_ENDIAN 1234
26 #define __BIG_ENDIAN  4321
27 #define __BYTE_ORDER __LITTLE_ENDIAN
28 #endif
29
30 #if __BYTE_ORDER == __BIG_ENDIAN
31 #define DEMUXER_SEQ_HEAD 0x000001B3
32 #else
33 #define DEMUXER_SEQ_HEAD 0xB3010000
34 #endif
35
36 #define SEEK_THRESHOLD 150000 // About 1.5 seconds
37
38 const int Demuxer::FrameRates[9] = { 0, 23, 24, 25, 29, 30, 50, 59, 60 };
39 const ULLONG Demuxer::PESPacket::PTS_INVALID = (1LL << 33);
40
41 Demuxer* Demuxer::instance = NULL;
42
43 Demuxer::Demuxer()
44 {
45   if (instance) return;
46   instance = this;
47   initted = false;
48   callback = NULL;
49   arcnt = 0;
50   vid_seeking = aud_seeking = false;
51   video_pts = audio_pts = 0;
52 }
53
54 Demuxer::~Demuxer()
55 {
56   shutdown();
57   instance = NULL;
58 }
59
60 Demuxer* Demuxer::getInstance()
61 {
62   return instance;
63 }
64
65 int Demuxer::init(Callback* tcallback, DrainTarget* audio, DrainTarget* video)
66 {
67   if (!initted)
68   {
69     if ( !videostream.init(video, demuxMemoryV) ||
70          !audiostream.init(audio, demuxMemoryA))
71     {
72       Log::getInstance()->log("Demuxer", Log::CRIT,
73                               "Failed to initialize demuxer");
74       shutdown();
75       return 0;
76     }
77   }
78
79   reset();
80   initted = true;
81   callback = tcallback;
82   return 1;
83 }
84
85 void Demuxer::reset()
86 {
87   flush();
88   video_current = audio_current = -1;
89   horizontal_size = vertical_size = 0;
90   aspect_ratio = (enum AspectRatio) 0;
91   frame_rate = bit_rate = 0;
92 }
93
94 int Demuxer::shutdown()
95 {
96   videostream.shutdown();
97   audiostream.shutdown();
98   initted = false;
99   return 1;
100 }
101
102 void Demuxer::flush()
103 {
104   videostream.flush();
105   audiostream.flush();
106 }
107
108 void Demuxer::flushAudio()
109 {
110   audiostream.flush();
111 }
112
113 void Demuxer::seek()
114 {
115   vid_seeking = aud_seeking = true;
116   video_pts = audio_pts = 0;
117 }
118
119 void Demuxer::setAudioStream(int id)
120 {
121   audio_current = id;
122 }
123
124 void Demuxer::setVideoStream(int id)
125 {
126   video_current = id;
127 }
128
129 void Demuxer::setAspectRatio(enum AspectRatio ar)
130 {
131   if (aspect_ratio != ar)
132   {
133     Log::getInstance()->log("Demux", Log::DEBUG,
134                             "Aspect ratio difference signalled");
135     if (++arcnt > 3) // avoid changing aspect ratio if glitch in signal
136     {
137       arcnt = 0;
138       aspect_ratio = ar;
139       callback->call(this);
140     }
141   }
142   else
143     arcnt = 0;
144 }
145
146 int Demuxer::writeAudio()
147 {
148   return audiostream.drain();
149 }
150
151 int Demuxer::writeVideo()
152 {
153   return videostream.drain();
154 }
155
156 Demuxer::PESPacket::PESPacket()
157 {
158   data[0] = 0x00;
159   data[1] = 0x00;
160   data[2] = 0x01;
161   init(0);
162 }
163
164 void Demuxer::PESPacket::init(UCHAR type)
165 {
166   length = submitted = 0;
167   size = 6; closed = false;
168   data[3] = type;
169   data[4] = data[5] = 0;
170   packetType = type;
171   pts = PTS_INVALID;
172   seq_header = false;
173 }
174
175 int Demuxer::PESPacket::write(UCHAR *buf, int len)
176 {
177   if (closed) return 0;
178   if (length + len > 0xFFFA) return 0;
179   memcpy(data+length+6, buf, len);
180   length += len;
181   size += len;
182   data[4] = (length >> 8);
183   data[5] = (length & 0xFF);
184   return 1;
185 }
186 #ifndef NEW_DEMUXER
187 int Demuxer::PESPacket::submit()
188 #else
189 int Demuxer::PESPacket::submit(ULLONG cur_pos)
190 #endif
191 {
192   if (submitted >= size) return 0;
193   if (!closed) parseDetails();
194
195   closed = true;
196   Demuxer* dx = Demuxer::getInstance();
197   int sent;
198   if (packetType >= PESTYPE_VID0 && packetType <= PESTYPE_VIDMAX)
199   {
200     if (dx->video_current == -1) dx->video_current = packetType;
201     if (dx->video_current == packetType && !dx->vid_seeking)
202 #ifndef NEW_DEMUXER
203       sent = dx->videostream.put(data+submitted, size-submitted);
204 #else
205       sent = dx->videostream.put(data+submitted, size-submitted,cur_pos);
206 #endif
207     else
208       sent = size-submitted;
209   }
210   else if (packetType >= PESTYPE_AUD0 && packetType <= PESTYPE_AUDMAX)
211   {
212     if (dx->audio_current == -1) dx->audio_current = packetType;
213     if (dx->audio_current == packetType && !dx->aud_seeking)
214 #ifndef NEW_DEMUXER
215       sent = dx->audiostream.put(data+submitted, size-submitted);
216 #else
217       sent = dx->audiostream.put(data+submitted, size-submitted,cur_pos);
218 #endif
219     else
220       sent = size-submitted;
221   }
222   else
223   {
224     sent = size-submitted;
225   }
226
227   submitted += sent;
228   if (submitted < size) // Stream is full.
229     return 0;
230   else
231     return 1;
232 }
233
234 void Demuxer::PESPacket::parseDetails()
235 {
236   Demuxer* dx = Demuxer::getInstance();
237   if (packetType >= PESTYPE_AUD0 && packetType <= PESTYPE_AUDMAX)
238   {
239     // Extract audio PTS if it exists
240     if ( data[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
241     {
242       dx->audio_pts = pts = ( (ULLONG)(data[9] & 0x0E)  << 29 ) |
243                             ( (ULLONG)(data[10])        << 22 ) |
244                             ( (ULLONG)(data[11] & 0xFE) << 14 ) |
245                             ( (ULLONG)(data[12])        <<  7 ) |
246                             ( (ULLONG)(data[13] & 0xFE) >>  1 );
247
248       // We continue to seek on the audio if the video PTS that we
249       // are trying to match is ahead of the audio PTS by at most
250       // SEEK_THRESHOLD. We consider the possibility of PTS wrap.
251       if (dx->aud_seeking && !dx->vid_seeking &&
252           !( (dx->video_pts_seek > dx->audio_pts &&
253               dx->video_pts_seek - dx->audio_pts < SEEK_THRESHOLD)
254               ||
255              (dx->video_pts_seek < dx->audio_pts &&
256               dx->video_pts_seek + (1LL<<33) -
257                                    dx->audio_pts < SEEK_THRESHOLD) ))
258       {
259         dx->aud_seeking = 0;
260         Log::getInstance()->log("Demuxer", Log::DEBUG,
261             "Leaving  audio sync: Audio PTS = %llu", dx->audio_pts);
262       }
263     }
264   }
265   else if (packetType >= PESTYPE_VID0 && packetType <= PESTYPE_VIDMAX)
266   {
267     // Extract video PTS if it exists
268     if ( data[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
269     {
270       dx->video_pts = pts = ( (ULLONG)(data[9] & 0x0E)  << 29 ) |
271                             ( (ULLONG)(data[10])        << 22 ) |
272                             ( (ULLONG)(data[11] & 0xFE) << 14 ) |
273                             ( (ULLONG)(data[12])        <<  7 ) |
274                             ( (ULLONG)(data[13] & 0xFE) >>  1 );
275     }
276
277     // Now, scan for a sequence header and extract information
278     UINT pos = 9; // Start searching from byte 9
279     while (pos < size - 5)
280     {
281       UINT pattern = *(UINT*)(data+pos);
282       if (pattern == DEMUXER_SEQ_HEAD)
283       {
284         seq_header = true;
285         pos += 4;
286         if (pos+6 >= size) return;
287         dx->horizontal_size = ((int)data[pos] << 4) | ((int)data[pos+1] >> 4);
288         dx->vertical_size = (((int)data[pos+1] & 0xf) << 8) | (int)data[pos+2];
289         dx->setAspectRatio((enum AspectRatio)(data[pos+3] >> 4));
290         dx->frame_rate = data[pos+3] & 0x0f;
291         if (dx->frame_rate >= 1 && dx->frame_rate <= 8)
292           dx->frame_rate = FrameRates[dx->frame_rate];
293         else
294           dx->frame_rate = 0;
295         dx->bit_rate = ((int)data[pos+4] << 10) |
296                    ((int)data[pos+5] << 2) |
297                    ((int)data[pos+6] >> 6);
298         if (dx->vid_seeking)
299         {
300           dx->vid_seeking = 0;
301           dx->video_pts_seek = dx->video_pts;
302           Log::getInstance()->log("Demuxer", Log::DEBUG,
303               "Entering audio sync: Video PTS = %llu", dx->video_pts);
304           Log::getInstance()->log("Demuxer", Log::DEBUG,
305               "Entering audio sync: Audio PTS = %llu", dx->audio_pts);
306         }
307         return;
308       }
309       else pos++;
310     }
311   }
312 }