]> git.vomp.tv Git - vompclient.git/blob - demuxer.cc
Demuxer frame counting: first step
[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 }
173
174 int Demuxer::PESPacket::write(UCHAR *buf, int len)
175 {
176   if (closed) return 0;
177   if (length + len > 0xFFFA) return 0;
178   memcpy(data+length+6, buf, len);
179   length += len;
180   size += len;
181   data[4] = (length >> 8);
182   data[5] = (length & 0xFF);
183   return 1;
184 }
185 #ifndef NEW_DEMUXER
186 int Demuxer::PESPacket::submit()
187 #else
188 int Demuxer::PESPacket::submit(ULLONG cur_pos)
189 #endif
190 {
191   if (submitted >= size) return 0;
192   if (!closed) parseDetails();
193
194   closed = true;
195   Demuxer* dx = Demuxer::getInstance();
196   int sent;
197   if (packetType >= PESTYPE_VID0 && packetType <= PESTYPE_VIDMAX)
198   {
199     if (dx->video_current == -1) dx->video_current = packetType;
200     if (dx->video_current == packetType && !dx->vid_seeking)
201 #ifndef NEW_DEMUXER
202       sent = dx->videostream.put(data+submitted, size-submitted);
203 #else
204       sent = dx->videostream.put(data+submitted, size-submitted,cur_pos);
205 #endif
206     else
207       sent = size-submitted;
208   }
209   else if (packetType >= PESTYPE_AUD0 && packetType <= PESTYPE_AUDMAX)
210   {
211     if (dx->audio_current == -1) dx->audio_current = packetType;
212     if (dx->audio_current == packetType && !dx->aud_seeking)
213 #ifndef NEW_DEMUXER
214       sent = dx->audiostream.put(data+submitted, size-submitted);
215 #else
216       sent = dx->audiostream.put(data+submitted, size-submitted,cur_pos);
217 #endif
218     else
219       sent = size-submitted;
220   }
221   else
222   {
223     sent = size-submitted;
224   }
225
226   submitted += sent;
227   if (submitted < size) // Stream is full.
228     return 0;
229   else
230     return 1;
231 }
232
233 void Demuxer::PESPacket::parseDetails()
234 {
235   Demuxer* dx = Demuxer::getInstance();
236   if (packetType >= PESTYPE_AUD0 && packetType <= PESTYPE_AUDMAX)
237   {
238     // Extract audio PTS if it exists
239     if ( data[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
240     {
241       dx->audio_pts = pts = ( (ULLONG)(data[9] & 0x0E)  << 29 ) |
242                             ( (ULLONG)(data[10])        << 22 ) |
243                             ( (ULLONG)(data[11] & 0xFE) << 14 ) |
244                             ( (ULLONG)(data[12])        <<  7 ) |
245                             ( (ULLONG)(data[13] & 0xFE) >>  1 );
246
247       // We continue to seek on the audio if the video PTS that we
248       // are trying to match is ahead of the audio PTS by at most
249       // SEEK_THRESHOLD. We consider the possibility of PTS wrap.
250       if (dx->aud_seeking && !dx->vid_seeking &&
251           !( (dx->video_pts_seek > dx->audio_pts &&
252               dx->video_pts_seek - dx->audio_pts < SEEK_THRESHOLD)
253               ||
254              (dx->video_pts_seek < dx->audio_pts &&
255               dx->video_pts_seek + (1LL<<33) -
256                                    dx->audio_pts < SEEK_THRESHOLD) ))
257       {
258         dx->aud_seeking = 0;
259         Log::getInstance()->log("Demuxer", Log::DEBUG,
260             "Leaving  audio sync: Audio PTS = %llu", dx->audio_pts);
261       }
262     }
263   }
264   else if (packetType >= PESTYPE_VID0 && packetType <= PESTYPE_VIDMAX)
265   {
266     // Extract video PTS if it exists
267     if ( data[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
268     {
269       dx->video_pts = pts = ( (ULLONG)(data[9] & 0x0E)  << 29 ) |
270                             ( (ULLONG)(data[10])        << 22 ) |
271                             ( (ULLONG)(data[11] & 0xFE) << 14 ) |
272                             ( (ULLONG)(data[12])        <<  7 ) |
273                             ( (ULLONG)(data[13] & 0xFE) >>  1 );
274     }
275
276     // Now, scan for a sequence header and extract information
277     UINT pos = 9; // Start searching from byte 9
278     while (pos < size - 5)
279     {
280       UINT pattern = *(UINT*)(data+pos);
281       if (pattern == DEMUXER_SEQ_HEAD)
282       {
283         pos += 4;
284         if (pos+6 >= size) return;
285         dx->horizontal_size = ((int)data[pos] << 4) | ((int)data[pos+1] >> 4);
286         dx->vertical_size = (((int)data[pos+1] & 0xf) << 8) | (int)data[pos+2];
287         dx->setAspectRatio((enum AspectRatio)(data[pos+3] >> 4));
288         dx->frame_rate = data[pos+3] & 0x0f;
289         if (dx->frame_rate >= 1 && dx->frame_rate <= 8)
290           dx->frame_rate = FrameRates[dx->frame_rate];
291         else
292           dx->frame_rate = 0;
293         dx->bit_rate = ((int)data[pos+4] << 10) |
294                    ((int)data[pos+5] << 2) |
295                    ((int)data[pos+6] >> 6);
296         if (dx->vid_seeking)
297         {
298           dx->vid_seeking = 0;
299           dx->video_pts_seek = dx->video_pts;
300           Log::getInstance()->log("Demuxer", Log::DEBUG,
301               "Entering audio sync: Video PTS = %llu", dx->video_pts);
302           Log::getInstance()->log("Demuxer", Log::DEBUG,
303               "Entering audio sync: Audio PTS = %llu", dx->audio_pts);
304         }
305         return;
306       }
307       else pos++;
308     }
309   }
310 }