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