/src/vlc/modules/demux/pva.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * pva.c: PVA demuxer |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2004 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Laurent Aimar <fenrir@via.ecp.fr> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program; if not, write to the Free Software Foundation, |
20 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
21 | | *****************************************************************************/ |
22 | | |
23 | | /***************************************************************************** |
24 | | * Preamble |
25 | | *****************************************************************************/ |
26 | | |
27 | | #ifdef HAVE_CONFIG_H |
28 | | # include "config.h" |
29 | | #endif |
30 | | |
31 | | #include <vlc_common.h> |
32 | | #include <vlc_plugin.h> |
33 | | #include <vlc_demux.h> |
34 | | |
35 | | #include "mpeg/pes.h" |
36 | | |
37 | | /***************************************************************************** |
38 | | * Module descriptor |
39 | | *****************************************************************************/ |
40 | | static int Open ( vlc_object_t * ); |
41 | | static void Close ( vlc_object_t * ); |
42 | | |
43 | 138 | vlc_module_begin () |
44 | 69 | set_description( N_("PVA demuxer" ) ) |
45 | 69 | set_capability( "demux", 10 ) |
46 | 69 | set_subcategory( SUBCAT_INPUT_DEMUX ) |
47 | 69 | set_callbacks( Open, Close ) |
48 | 69 | add_shortcut( "pva" ) |
49 | 69 | add_file_extension("pva") |
50 | 69 | vlc_module_end () |
51 | | |
52 | | /***************************************************************************** |
53 | | * Local prototypes |
54 | | *****************************************************************************/ |
55 | | |
56 | | typedef struct |
57 | | { |
58 | | es_out_id_t *p_video; |
59 | | es_out_id_t *p_audio; |
60 | | |
61 | | /* counter */ |
62 | | int i_vc; |
63 | | int i_ac; |
64 | | |
65 | | /* audio/video block */ |
66 | | block_t *p_pes; /* audio */ |
67 | | block_t *p_es; /* video */ |
68 | | |
69 | | int64_t b_pcr_audio; |
70 | | } demux_sys_t; |
71 | | |
72 | | static int Demux ( demux_t *p_demux ); |
73 | | static int Control ( demux_t *p_demux, int i_query, va_list args ); |
74 | | |
75 | | static int ReSynch ( demux_t * ); |
76 | | static void ParsePES( demux_t * ); |
77 | | |
78 | | /***************************************************************************** |
79 | | * Open |
80 | | *****************************************************************************/ |
81 | | static int Open( vlc_object_t *p_this ) |
82 | 10.1k | { |
83 | 10.1k | demux_t *p_demux = (demux_t*)p_this; |
84 | 10.1k | demux_sys_t *p_sys; |
85 | 10.1k | es_format_t fmt; |
86 | 10.1k | const uint8_t *p_peek; |
87 | | |
88 | 10.1k | if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) return VLC_EGENERIC; |
89 | 10.1k | if( p_peek[0] != 'A' || p_peek[1] != 'V' || p_peek[4] != 0x55 ) |
90 | 5.77k | { |
91 | | /* In case we had forced this demuxer we try to resynch */ |
92 | 5.77k | if( !p_demux->obj.force || ReSynch( p_demux ) ) |
93 | 1.09k | return VLC_EGENERIC; |
94 | 5.77k | } |
95 | | |
96 | 9.06k | p_sys = malloc( sizeof( demux_sys_t ) ); |
97 | 9.06k | if( unlikely(p_sys == NULL) ) |
98 | 0 | return VLC_ENOMEM; |
99 | | |
100 | | /* Fill p_demux field */ |
101 | 9.06k | p_demux->pf_demux = Demux; |
102 | 9.06k | p_demux->pf_control = Control; |
103 | 9.06k | p_demux->p_sys = p_sys; |
104 | | |
105 | | /* Register one audio and one video stream */ |
106 | 9.06k | es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_MPGA ); |
107 | 9.06k | fmt.b_packetized = false; |
108 | 9.06k | p_sys->p_audio = es_out_Add( p_demux->out, &fmt ); |
109 | | |
110 | 9.06k | es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_MPGV ); |
111 | 9.06k | fmt.b_packetized = false; |
112 | 9.06k | p_sys->p_video = es_out_Add( p_demux->out, &fmt ); |
113 | | |
114 | 9.06k | p_sys->i_vc = -1; |
115 | 9.06k | p_sys->i_ac = -1; |
116 | 9.06k | p_sys->p_pes = NULL; |
117 | 9.06k | p_sys->p_es = NULL; |
118 | | |
119 | 9.06k | p_sys->b_pcr_audio = false; |
120 | | |
121 | 9.06k | return VLC_SUCCESS; |
122 | 9.06k | } |
123 | | |
124 | | /***************************************************************************** |
125 | | * Close |
126 | | *****************************************************************************/ |
127 | | static void Close( vlc_object_t *p_this ) |
128 | 9.06k | { |
129 | 9.06k | demux_t *p_demux = (demux_t*)p_this; |
130 | 9.06k | demux_sys_t *p_sys = p_demux->p_sys; |
131 | | |
132 | 9.06k | block_ChainRelease( p_sys->p_es ); |
133 | 9.06k | block_ChainRelease( p_sys->p_pes ); |
134 | | |
135 | 9.06k | free( p_sys ); |
136 | 9.06k | } |
137 | | |
138 | | |
139 | | /***************************************************************************** |
140 | | * Demux: |
141 | | ***************************************************************************** |
142 | | * See http://multimedia.cx/mirror/av_format_v1.pdf |
143 | | *****************************************************************************/ |
144 | | static int Demux( demux_t *p_demux ) |
145 | 2.27M | { |
146 | 2.27M | demux_sys_t *p_sys = p_demux->p_sys; |
147 | | |
148 | 2.27M | const uint8_t *p_peek; |
149 | 2.27M | int i_size; |
150 | 2.27M | block_t *p_frame; |
151 | 2.27M | ts_90khz_t i_pts; |
152 | 2.27M | int i_skip; |
153 | | |
154 | 2.27M | if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) |
155 | 7.82k | { |
156 | 7.82k | msg_Warn( p_demux, "eof ?" ); |
157 | 7.82k | return VLC_DEMUXER_EOF; |
158 | 7.82k | } |
159 | 2.26M | if( p_peek[0] != 'A' || p_peek[1] != 'V' || p_peek[4] != 0x55 ) |
160 | 1.61M | { |
161 | 1.61M | msg_Warn( p_demux, "lost synchro" ); |
162 | 1.61M | if( ReSynch( p_demux ) ) |
163 | 1.08k | { |
164 | 1.08k | return VLC_DEMUXER_EGENERIC; |
165 | 1.08k | } |
166 | 1.61M | if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) |
167 | 70 | { |
168 | 70 | msg_Warn( p_demux, "eof ?" ); |
169 | 70 | return VLC_DEMUXER_EOF; |
170 | 70 | } |
171 | 1.61M | } |
172 | | |
173 | 2.26M | i_size = GetWBE( &p_peek[6] ); |
174 | 2.26M | switch( p_peek[2] ) |
175 | 2.26M | { |
176 | 385k | case 0x01: /* VideoStream */ |
177 | 385k | if( p_sys->i_vc < 0 ) |
178 | 4.92k | { |
179 | 4.92k | msg_Dbg( p_demux, "first packet for video" ); |
180 | 4.92k | } |
181 | 380k | else if( ((p_sys->i_vc + 1)&0xff) != p_peek[3] ) |
182 | 233k | { |
183 | 233k | msg_Dbg( p_demux, "packet lost (video)" ); |
184 | 233k | if( p_sys->p_es ) |
185 | 57.0k | { |
186 | 57.0k | block_ChainRelease( p_sys->p_es ); |
187 | 57.0k | p_sys->p_es = NULL; |
188 | 57.0k | } |
189 | 233k | } |
190 | 385k | p_sys->i_vc = p_peek[3]; |
191 | | |
192 | | /* read the PTS and potential extra bytes TODO: make it a bit more optimised */ |
193 | 385k | i_pts = TS_90KHZ_INVALID; |
194 | 385k | i_skip = 8; |
195 | 385k | if( p_peek[5]&0x10 ) |
196 | 338k | { |
197 | 338k | int i_pre = p_peek[5]&0x3; |
198 | | |
199 | 338k | if( ( p_frame = vlc_stream_Block( p_demux->s, 8 + 4 + i_pre ) ) ) |
200 | 338k | { |
201 | 338k | i_pts = GetDWBE( &p_frame->p_buffer[8] ); |
202 | 338k | if( p_frame->i_buffer > 12 ) |
203 | 317k | { |
204 | 317k | p_frame->p_buffer += 12; |
205 | 317k | p_frame->i_buffer -= 12; |
206 | 317k | block_ChainAppend( &p_sys->p_es, p_frame ); |
207 | 317k | } |
208 | 21.5k | else |
209 | 21.5k | { |
210 | 21.5k | block_Release( p_frame ); |
211 | 21.5k | } |
212 | 338k | } |
213 | 338k | i_size -= 4 + i_pre; |
214 | 338k | i_skip = 0; |
215 | | /* Set PCR */ |
216 | 338k | if( ( p_frame = p_sys->p_es ) ) |
217 | 327k | { |
218 | | |
219 | 327k | if( p_frame->i_pts != VLC_TICK_INVALID && !p_sys->b_pcr_audio ) |
220 | 59.2k | { |
221 | 59.2k | es_out_SetPCR( p_demux->out, p_frame->i_pts); |
222 | 59.2k | } |
223 | | |
224 | 327k | p_frame = block_ChainGather( p_frame ); |
225 | 327k | if( likely(p_frame) ) |
226 | 327k | { |
227 | 327k | if( likely(p_sys->p_video) ) |
228 | 327k | es_out_Send( p_demux->out, p_sys->p_video, p_frame ); |
229 | 0 | else |
230 | 0 | block_Release( p_frame ); |
231 | 327k | } |
232 | | |
233 | 327k | p_sys->p_es = NULL; |
234 | 327k | } |
235 | 338k | } |
236 | | |
237 | 385k | if( ( p_frame = vlc_stream_Block( p_demux->s, i_size + i_skip ) ) ) |
238 | 188k | { |
239 | 188k | p_frame->p_buffer += i_skip; |
240 | 188k | p_frame->i_buffer -= i_skip; |
241 | 188k | if( i_pts != TS_90KHZ_INVALID ) |
242 | 142k | p_frame->i_pts = FROM_SCALE(i_pts); |
243 | 188k | block_ChainAppend( &p_sys->p_es, p_frame ); |
244 | 188k | } |
245 | 385k | break; |
246 | | |
247 | 1.87M | case 0x02: /* MainAudioStream */ |
248 | 1.87M | if( p_sys->i_ac < 0 ) |
249 | 5.59k | { |
250 | 5.59k | msg_Dbg( p_demux, "first packet for audio" ); |
251 | 5.59k | } |
252 | 1.86M | else if( ((p_sys->i_ac + 1)&0xff) != p_peek[3] ) |
253 | 925k | { |
254 | 925k | msg_Dbg( p_demux, "packet lost (audio)" ); |
255 | 925k | if( p_sys->p_pes ) |
256 | 925k | { |
257 | 925k | block_ChainRelease( p_sys->p_pes ); |
258 | 925k | p_sys->p_pes = NULL; |
259 | 925k | } |
260 | 925k | } |
261 | 1.87M | p_sys->i_ac = p_peek[3]; |
262 | | |
263 | 1.87M | if( p_peek[5]&0x10 && p_sys->p_pes ) |
264 | 604k | { |
265 | 604k | ParsePES( p_demux ); |
266 | 604k | } |
267 | 1.87M | if( ( p_frame = vlc_stream_Block( p_demux->s, i_size + 8 ) ) ) |
268 | 1.87M | { |
269 | 1.87M | p_frame->p_buffer += 8; |
270 | 1.87M | p_frame->i_buffer -= 8; |
271 | | /* XXX this a hack, some streams aren't compliant and |
272 | | * don't set pes_start flag */ |
273 | 1.87M | if( p_sys->p_pes && p_frame->i_buffer > 4 && |
274 | 317k | p_frame->p_buffer[0] == 0x00 && |
275 | 298k | p_frame->p_buffer[1] == 0x00 && |
276 | 290k | p_frame->p_buffer[2] == 0x01 ) |
277 | 271k | { |
278 | 271k | ParsePES( p_demux ); |
279 | 271k | } |
280 | 1.87M | block_ChainAppend( &p_sys->p_pes, p_frame ); |
281 | 1.87M | } |
282 | 1.87M | break; |
283 | | |
284 | 10.6k | default: |
285 | 10.6k | msg_Warn( p_demux, "unknown id=0x%x", p_peek[2] ); |
286 | 10.6k | if( vlc_stream_Read( p_demux->s, NULL, i_size + 8 ) != (i_size + 8) ) |
287 | 87 | return VLC_DEMUXER_EOF; |
288 | 10.6k | break; |
289 | 2.26M | } |
290 | 2.26M | return VLC_DEMUXER_SUCCESS; |
291 | 2.26M | } |
292 | | |
293 | | /***************************************************************************** |
294 | | * Control: |
295 | | *****************************************************************************/ |
296 | | static int Control( demux_t *p_demux, int i_query, va_list args ) |
297 | 0 | { |
298 | | /* demux_sys_t *p_sys = p_demux->p_sys; */ |
299 | 0 | double f, *pf; |
300 | 0 | int64_t i64; |
301 | 0 | switch( i_query ) |
302 | 0 | { |
303 | 0 | case DEMUX_CAN_SEEK: |
304 | 0 | return vlc_stream_vaControl( p_demux->s, i_query, args ); |
305 | | |
306 | 0 | case DEMUX_GET_POSITION: |
307 | 0 | if( ( i64 = stream_Size( p_demux->s ) ) > 0 ) |
308 | 0 | { |
309 | 0 | pf = va_arg( args, double * ); |
310 | 0 | double current = vlc_stream_Tell( p_demux->s ); |
311 | 0 | *pf = current / (double)i64; |
312 | 0 | return VLC_SUCCESS; |
313 | 0 | } |
314 | 0 | return VLC_EGENERIC; |
315 | | |
316 | 0 | case DEMUX_SET_POSITION: |
317 | 0 | f = va_arg( args, double ); |
318 | 0 | i64 = stream_Size( p_demux->s ); |
319 | |
|
320 | 0 | if( vlc_stream_Seek( p_demux->s, (int64_t)(i64 * f) ) || ReSynch( p_demux ) ) |
321 | 0 | { |
322 | 0 | return VLC_EGENERIC; |
323 | 0 | } |
324 | 0 | return VLC_SUCCESS; |
325 | | |
326 | | #if 0 |
327 | | case DEMUX_GET_TIME: |
328 | | pi64 = va_arg( args, vlc_tick_t * ); |
329 | | if( p_sys->i_time < 0 ) |
330 | | { |
331 | | *pi64 = 0; |
332 | | return VLC_EGENERIC; |
333 | | } |
334 | | *pi64 = p_sys->i_time; |
335 | | return VLC_SUCCESS; |
336 | | |
337 | | #if 0 |
338 | | case DEMUX_GET_LENGTH: |
339 | | pi64 = va_arg( args, vlc_tick_t * ); |
340 | | if( p_sys->i_mux_rate > 0 ) |
341 | | { |
342 | | *pi64 = vlc_tick_from_samples( stream_Size( p_demux->s ) / 50, p_sys->i_mux_rate); |
343 | | return VLC_SUCCESS; |
344 | | } |
345 | | *pi64 = 0; |
346 | | return VLC_EGENERIC; |
347 | | |
348 | | #endif |
349 | | case DEMUX_GET_FPS: |
350 | | pf = va_arg( args, double * ); |
351 | | *pf = (double)1000000.0 / (double)p_sys->i_pcr_inc; |
352 | | return VLC_SUCCESS; |
353 | | #endif |
354 | 0 | case DEMUX_CAN_PAUSE: |
355 | 0 | case DEMUX_SET_PAUSE_STATE: |
356 | 0 | case DEMUX_CAN_CONTROL_PACE: |
357 | 0 | case DEMUX_GET_PTS_DELAY: |
358 | 0 | return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args ); |
359 | | |
360 | 0 | case DEMUX_SET_TIME: |
361 | 0 | default: |
362 | 0 | return VLC_EGENERIC; |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | /***************************************************************************** |
367 | | * ReSynch: |
368 | | *****************************************************************************/ |
369 | | static int ReSynch( demux_t *p_demux ) |
370 | 1.62M | { |
371 | 1.62M | for( ;; ) |
372 | 1.62M | { |
373 | 1.62M | const uint8_t *p_peek; |
374 | 1.62M | int i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 1024 ); |
375 | 1.62M | if( i_peek < 8 ) |
376 | 1.14k | break; |
377 | | |
378 | 1.62M | int i_skip = 0; |
379 | | |
380 | 44.0M | while( i_skip < i_peek - 5 ) |
381 | 44.0M | { |
382 | 44.0M | if( p_peek[0] == 'A' && p_peek[1] == 'V' && p_peek[4] == 0x55 ) |
383 | 1.62M | { |
384 | 1.62M | if( i_skip > 0 |
385 | 1.62M | && vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip ) |
386 | 0 | return VLC_EGENERIC; |
387 | 1.62M | return VLC_SUCCESS; |
388 | 1.62M | } |
389 | 42.4M | p_peek++; |
390 | 42.4M | i_skip++; |
391 | 42.4M | } |
392 | | |
393 | 7.67k | if( vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip ) |
394 | 0 | break; |
395 | 7.67k | } |
396 | | |
397 | 1.14k | return VLC_EGENERIC; |
398 | 1.62M | } |
399 | | |
400 | | static void ParsePES( demux_t *p_demux ) |
401 | 876k | { |
402 | 876k | demux_sys_t *p_sys = p_demux->p_sys; |
403 | 876k | block_t *p_pes = p_sys->p_pes; |
404 | 876k | uint8_t hdr[20]; |
405 | | |
406 | 876k | unsigned i_skip; |
407 | 876k | ts_90khz_t i_dts; |
408 | 876k | ts_90khz_t i_pts; |
409 | | |
410 | 876k | p_sys->p_pes = NULL; |
411 | | |
412 | | /* FIXME find real max size */ |
413 | 876k | size_t hdr_read = block_ChainExtract( p_pes, hdr, ARRAY_SIZE(hdr) ); |
414 | | |
415 | | /* See ยง2.4.3.6 of ISO 13818-1 */ |
416 | 876k | if( hdr_read < 9 ) { |
417 | 8.24k | msg_Warn( p_demux, "invalid hdr size (%zu)", hdr_read); |
418 | 8.24k | block_ChainRelease( p_pes ); |
419 | 8.24k | return; |
420 | 8.24k | } |
421 | 867k | if( hdr[0] != 0 || hdr[1] != 0 || hdr[2] != 1 ) { |
422 | 25.5k | msg_Warn( p_demux, "invalid hdr [0x%2.2x:%2.2x:%2.2x]", |
423 | 25.5k | hdr[0], hdr[1], hdr[2] ); |
424 | 25.5k | block_ChainRelease( p_pes ); |
425 | 25.5k | return; |
426 | 25.5k | } |
427 | | // hdr[4] i_pes_size, 2 bytes |
428 | | // hdr[6] Marker -> original_or_copy |
429 | | |
430 | | /* we assume mpeg2 PES */ |
431 | 842k | i_skip = hdr[8] + 9; |
432 | | |
433 | 842k | p_pes = block_ChainGather( p_pes ); |
434 | 842k | if( unlikely(p_pes == NULL) ) |
435 | 0 | return; |
436 | 842k | if( p_pes->i_buffer <= i_skip ) |
437 | 37.4k | { |
438 | 37.4k | block_ChainRelease( p_pes ); |
439 | 37.4k | return; |
440 | 37.4k | } |
441 | | |
442 | 804k | p_pes->i_buffer -= i_skip; |
443 | 804k | p_pes->p_buffer += i_skip; |
444 | | |
445 | 804k | if( hdr[7]&0x80 && hdr_read >= (9+1+5) ) /* has pts */ |
446 | 759k | { |
447 | 759k | i_pts = GetPESTimestamp( &hdr[9] ); |
448 | 759k | p_pes->i_pts = FROM_SCALE(i_pts); |
449 | | |
450 | 759k | if( hdr[7]&0x40 && hdr_read >= (14+1+5) ) /* has dts */ |
451 | 601k | { |
452 | 601k | i_dts = GetPESTimestamp( &hdr[14] ); |
453 | 601k | p_pes->i_dts = FROM_SCALE(i_dts); |
454 | 601k | } |
455 | 759k | } |
456 | | |
457 | | /* Set PCR */ |
458 | 804k | if( p_pes->i_pts != VLC_TICK_INVALID ) |
459 | 759k | { |
460 | 759k | es_out_SetPCR( p_demux->out, p_pes->i_pts); |
461 | 759k | p_sys->b_pcr_audio = true; |
462 | 759k | } |
463 | 804k | if( likely(p_sys->p_audio) ) |
464 | 804k | es_out_Send( p_demux->out, p_sys->p_audio, p_pes ); |
465 | 0 | else |
466 | 0 | block_Release( p_pes ); |
467 | 804k | } |