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