/src/vlc/modules/demux/nsv.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * nsv.c: NullSoft Video demuxer. |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2004-2007 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 <limits.h> |
32 | | |
33 | | #include <vlc_common.h> |
34 | | #include <vlc_plugin.h> |
35 | | #include <vlc_demux.h> |
36 | | |
37 | | /* TODO: |
38 | | * - implement NSVf parsing (to get meta data) |
39 | | * - implement missing Control (and in the right way) |
40 | | * - ... |
41 | | */ |
42 | | |
43 | | /***************************************************************************** |
44 | | * Module descriptor |
45 | | *****************************************************************************/ |
46 | | static int Open ( vlc_object_t * ); |
47 | | static void Close ( vlc_object_t * ); |
48 | | |
49 | 4 | vlc_module_begin () |
50 | 2 | set_description( N_("NullSoft demuxer" ) ) |
51 | 2 | set_capability( "demux", 10 ) |
52 | 2 | set_subcategory( SUBCAT_INPUT_DEMUX ) |
53 | 2 | set_callbacks( Open, Close ) |
54 | 2 | add_shortcut( "nsv" ) |
55 | 2 | add_file_extension("nsv") |
56 | 2 | vlc_module_end () |
57 | | |
58 | | /***************************************************************************** |
59 | | * Local prototypes |
60 | | *****************************************************************************/ |
61 | | |
62 | | typedef struct |
63 | | { |
64 | | es_format_t fmt_audio; |
65 | | es_out_id_t *p_audio; |
66 | | |
67 | | es_format_t fmt_video; |
68 | | es_out_id_t *p_video; |
69 | | |
70 | | es_format_t fmt_sub; |
71 | | es_out_id_t *p_sub; |
72 | | |
73 | | vlc_tick_t i_pcr; |
74 | | vlc_tick_t i_time; |
75 | | vlc_tick_t i_pcr_inc; |
76 | | |
77 | | bool b_start_record; |
78 | | char *record_dir_path; |
79 | | } demux_sys_t; |
80 | | |
81 | | static int Demux ( demux_t *p_demux ); |
82 | | static int Control( demux_t *p_demux, int i_query, va_list args ); |
83 | | |
84 | | static int ReSynch( demux_t *p_demux ); |
85 | | |
86 | | static int ReadNSVf( demux_t *p_demux ); |
87 | | static int ReadNSVs( demux_t *p_demux ); |
88 | | |
89 | | /***************************************************************************** |
90 | | * Open |
91 | | *****************************************************************************/ |
92 | | static int Open( vlc_object_t *p_this ) |
93 | 39 | { |
94 | 39 | demux_t *p_demux = (demux_t*)p_this; |
95 | 39 | demux_sys_t *p_sys; |
96 | | |
97 | 39 | const uint8_t *p_peek; |
98 | | |
99 | 39 | if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) |
100 | 0 | return VLC_EGENERIC; |
101 | | |
102 | 39 | if( memcmp( p_peek, "NSVf", 4 ) && memcmp( p_peek, "NSVs", 4 ) ) |
103 | 39 | { |
104 | | /* In case we had force this demuxer we try to resynch */ |
105 | 39 | if( !p_demux->obj.force || ReSynch( p_demux ) ) |
106 | 39 | return VLC_EGENERIC; |
107 | 39 | } |
108 | | |
109 | 0 | p_sys = malloc( sizeof( demux_sys_t ) ); |
110 | 0 | if( unlikely(p_sys == NULL) ) |
111 | 0 | return VLC_ENOMEM; |
112 | | |
113 | | /* Fill p_demux field */ |
114 | 0 | p_demux->p_sys = p_sys; |
115 | 0 | p_demux->pf_demux = Demux; |
116 | 0 | p_demux->pf_control = Control; |
117 | |
|
118 | 0 | es_format_Init( &p_sys->fmt_audio, AUDIO_ES, 0 ); |
119 | 0 | p_sys->p_audio = NULL; |
120 | |
|
121 | 0 | es_format_Init( &p_sys->fmt_video, VIDEO_ES, 0 ); |
122 | 0 | p_sys->p_video = NULL; |
123 | |
|
124 | 0 | es_format_Init( &p_sys->fmt_sub, SPU_ES, 0 ); |
125 | 0 | p_sys->p_sub = NULL; |
126 | |
|
127 | 0 | p_sys->i_pcr = 0; |
128 | 0 | p_sys->i_time = 0; |
129 | 0 | p_sys->i_pcr_inc = 0; |
130 | |
|
131 | 0 | p_sys->b_start_record = false; |
132 | 0 | p_sys->record_dir_path = NULL; |
133 | |
|
134 | 0 | return VLC_SUCCESS; |
135 | 0 | } |
136 | | |
137 | | /***************************************************************************** |
138 | | * Close |
139 | | *****************************************************************************/ |
140 | | static void Close( vlc_object_t *p_this ) |
141 | 0 | { |
142 | 0 | demux_t *p_demux = (demux_t*)p_this; |
143 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
144 | |
|
145 | 0 | free( p_sys->record_dir_path ); |
146 | 0 | free( p_sys ); |
147 | 0 | } |
148 | | |
149 | | |
150 | | /***************************************************************************** |
151 | | * Demux: |
152 | | *****************************************************************************/ |
153 | | static int Demux( demux_t *p_demux ) |
154 | 0 | { |
155 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
156 | |
|
157 | 0 | uint8_t header[5]; |
158 | 0 | const uint8_t *p_peek; |
159 | |
|
160 | 0 | int i_size; |
161 | 0 | block_t *p_frame; |
162 | |
|
163 | 0 | for( ;; ) |
164 | 0 | { |
165 | 0 | if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) |
166 | 0 | return VLC_DEMUXER_EOF; |
167 | | |
168 | 0 | if( !memcmp( p_peek, "NSVf", 4 ) ) |
169 | 0 | { |
170 | 0 | if( ReadNSVf( p_demux ) ) |
171 | 0 | return VLC_DEMUXER_EGENERIC; |
172 | 0 | } |
173 | 0 | else if( !memcmp( p_peek, "NSVs", 4 ) ) |
174 | 0 | { |
175 | 0 | if( p_sys->b_start_record ) |
176 | 0 | { |
177 | | /* Enable recording once synchronized */ |
178 | 0 | vlc_stream_Control( p_demux->s, STREAM_SET_RECORD_STATE, true, |
179 | 0 | p_sys->record_dir_path, "nsv" ); |
180 | 0 | p_sys->b_start_record = false; |
181 | 0 | } |
182 | |
|
183 | 0 | if( ReadNSVs( p_demux ) ) |
184 | 0 | return VLC_DEMUXER_EGENERIC; |
185 | 0 | break; |
186 | 0 | } |
187 | 0 | else if( GetWLE( p_peek ) == 0xbeef ) |
188 | 0 | { |
189 | | /* Next frame of the current NSVs chunk */ |
190 | 0 | if( vlc_stream_Read( p_demux->s, NULL, 2 ) != 2 ) |
191 | 0 | { |
192 | 0 | msg_Warn( p_demux, "cannot read" ); |
193 | 0 | return VLC_DEMUXER_EOF; |
194 | 0 | } |
195 | 0 | break; |
196 | 0 | } |
197 | 0 | else |
198 | 0 | { |
199 | 0 | msg_Err( p_demux, "invalid signature 0x%x (%4.4s)", GetDWLE( p_peek ), (const char*)p_peek ); |
200 | 0 | if( ReSynch( p_demux ) ) |
201 | 0 | return VLC_DEMUXER_EGENERIC; |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | 0 | if( vlc_stream_Read( p_demux->s, header, 5 ) < 5 ) |
206 | 0 | { |
207 | 0 | msg_Warn( p_demux, "cannot read" ); |
208 | 0 | return VLC_DEMUXER_EOF; |
209 | 0 | } |
210 | | |
211 | | /* Set PCR */ |
212 | 0 | es_out_SetPCR( p_demux->out, VLC_TICK_0 + p_sys->i_pcr ); |
213 | | |
214 | | /* Read video */ |
215 | 0 | i_size = ( header[0] >> 4 ) | ( header[1] << 4 ) | ( header[2] << 12 ); |
216 | 0 | if( i_size > 0 ) |
217 | 0 | { |
218 | | /* extra data ? */ |
219 | 0 | if( (header[0]&0x0f) != 0x0 ) |
220 | 0 | { |
221 | 0 | uint8_t aux[6]; |
222 | 0 | int i_aux; |
223 | 0 | vlc_fourcc_t fcc; |
224 | 0 | if( vlc_stream_Read( p_demux->s, aux, 6 ) < 6 ) |
225 | 0 | { |
226 | 0 | msg_Warn( p_demux, "cannot read" ); |
227 | 0 | return VLC_DEMUXER_EOF; |
228 | 0 | } |
229 | 0 | i_aux = GetWLE( aux ); |
230 | 0 | fcc = VLC_FOURCC( aux[2], aux[3], aux[4], aux[5] ); |
231 | |
|
232 | 0 | msg_Dbg( p_demux, "Belekas: %d - size=%d fcc=%4.4s", |
233 | 0 | header[0]&0xf, i_aux, (char*)&fcc ); |
234 | |
|
235 | 0 | if( fcc == VLC_FOURCC( 'S', 'U', 'B', 'T' ) && i_aux > 2 ) |
236 | 0 | { |
237 | 0 | if( p_sys->p_sub == NULL ) |
238 | 0 | { |
239 | 0 | p_sys->fmt_sub.i_codec = VLC_FOURCC( 's', 'u', 'b', 't' ); |
240 | 0 | p_sys->p_sub = es_out_Add( p_demux->out, &p_sys->fmt_sub ); |
241 | 0 | if( p_sys->p_sub ) |
242 | 0 | es_out_Control( p_demux->out, ES_OUT_SET_ES, p_sys->p_sub ); |
243 | 0 | } |
244 | 0 | if( vlc_stream_Read( p_demux->s, NULL, 2 ) != 2 ) |
245 | 0 | return VLC_DEMUXER_EOF; |
246 | | |
247 | 0 | if( ( p_frame = vlc_stream_Block( p_demux->s, i_aux - 2 ) ) ) |
248 | 0 | { |
249 | 0 | uint8_t *p = p_frame->p_buffer; |
250 | |
|
251 | 0 | while( p < &p_frame->p_buffer[p_frame->i_buffer] && *p != 0 ) |
252 | 0 | { |
253 | 0 | p++; |
254 | 0 | } |
255 | 0 | if( *p == 0 && p + 1 < &p_frame->p_buffer[p_frame->i_buffer] ) |
256 | 0 | { |
257 | 0 | p_frame->i_buffer -= p + 1 - p_frame->p_buffer; |
258 | 0 | p_frame->p_buffer = p + 1; |
259 | 0 | } |
260 | | |
261 | | /* Skip the first part (it is the language name) */ |
262 | 0 | p_frame->i_pts = VLC_TICK_0 + p_sys->i_pcr; |
263 | 0 | p_frame->i_dts = VLC_TICK_0 + p_sys->i_pcr + VLC_TICK_FROM_SEC(4); |
264 | |
|
265 | 0 | if( p_sys->p_sub ) |
266 | 0 | es_out_Send( p_demux->out, p_sys->p_sub, p_frame ); |
267 | 0 | else |
268 | 0 | block_Release( p_frame ); |
269 | 0 | } |
270 | 0 | } |
271 | 0 | else |
272 | 0 | { |
273 | | /* We skip this extra data */ |
274 | 0 | if( vlc_stream_Read( p_demux->s, NULL, i_aux ) != i_aux ) |
275 | 0 | { |
276 | 0 | msg_Warn( p_demux, "cannot read" ); |
277 | 0 | return VLC_DEMUXER_EOF; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | i_size -= 6 + i_aux; |
281 | 0 | } |
282 | | |
283 | | /* msg_Dbg( p_demux, "frame video size=%d", i_size ); */ |
284 | 0 | if( i_size > 0 && ( p_frame = vlc_stream_Block( p_demux->s, i_size ) ) ) |
285 | 0 | { |
286 | 0 | p_frame->i_dts = VLC_TICK_0 + p_sys->i_pcr; |
287 | |
|
288 | 0 | if( p_sys->p_video ) |
289 | 0 | es_out_Send( p_demux->out, p_sys->p_video, p_frame ); |
290 | 0 | else |
291 | 0 | { |
292 | 0 | block_Release( p_frame ); |
293 | 0 | msg_Dbg( p_demux, "ignoring unsupported video frame (size=%d)", |
294 | 0 | i_size ); |
295 | 0 | } |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | | /* Read audio */ |
300 | 0 | i_size = header[3] | ( header[4] << 8 ); |
301 | 0 | if( i_size > 0 ) |
302 | 0 | { |
303 | | /* msg_Dbg( p_demux, "frame audio size=%d", i_size ); */ |
304 | 0 | if( p_sys->fmt_audio.i_codec == VLC_FOURCC( 'a', 'r', 'a', 'w' ) ) |
305 | 0 | { |
306 | 0 | uint8_t h[4]; |
307 | 0 | if( vlc_stream_Read( p_demux->s, h, 4 ) < 4 ) |
308 | 0 | return VLC_DEMUXER_EOF; |
309 | | |
310 | 0 | p_sys->fmt_audio.audio.i_channels = h[1]; |
311 | 0 | p_sys->fmt_audio.audio.i_rate = GetWLE( &h[2] ); |
312 | |
|
313 | 0 | i_size -= 4; |
314 | 0 | } |
315 | 0 | if( p_sys->p_audio == NULL ) |
316 | 0 | { |
317 | 0 | p_sys->p_audio = es_out_Add( p_demux->out, &p_sys->fmt_audio ); |
318 | 0 | } |
319 | |
|
320 | 0 | if( ( p_frame = vlc_stream_Block( p_demux->s, i_size ) ) ) |
321 | 0 | { |
322 | 0 | p_frame->i_dts = |
323 | 0 | p_frame->i_pts = VLC_TICK_0 + p_sys->i_pcr; |
324 | |
|
325 | 0 | if( p_sys->p_audio ) |
326 | 0 | es_out_Send( p_demux->out, p_sys->p_audio, p_frame ); |
327 | 0 | else |
328 | 0 | { |
329 | 0 | block_Release( p_frame ); |
330 | 0 | msg_Dbg( p_demux, "ignoring unsupported audio frame (size=%d)", |
331 | 0 | i_size ); |
332 | 0 | } |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | 0 | p_sys->i_pcr += p_sys->i_pcr_inc; |
337 | 0 | if( p_sys->i_time >= 0 ) |
338 | 0 | { |
339 | 0 | p_sys->i_time += p_sys->i_pcr_inc; |
340 | 0 | } |
341 | |
|
342 | 0 | return VLC_DEMUXER_SUCCESS; |
343 | 0 | } |
344 | | |
345 | | /***************************************************************************** |
346 | | * Control: |
347 | | *****************************************************************************/ |
348 | | static int Control( demux_t *p_demux, int i_query, va_list args ) |
349 | 0 | { |
350 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
351 | 0 | double f, *pf; |
352 | 0 | bool b_bool, *pb_bool; |
353 | 0 | int64_t i64; |
354 | |
|
355 | 0 | switch( i_query ) |
356 | 0 | { |
357 | 0 | case DEMUX_CAN_SEEK: |
358 | 0 | return vlc_stream_vaControl( p_demux->s, i_query, args ); |
359 | | |
360 | 0 | case DEMUX_GET_POSITION: |
361 | 0 | pf = va_arg( args, double * ); |
362 | 0 | i64 = stream_Size( p_demux->s ); |
363 | 0 | if( i64 > 0 ) |
364 | 0 | { |
365 | 0 | double current = vlc_stream_Tell( p_demux->s ); |
366 | 0 | *pf = current / (double)i64; |
367 | 0 | } |
368 | 0 | else |
369 | 0 | { |
370 | 0 | *pf = 0.0; |
371 | 0 | } |
372 | 0 | return VLC_SUCCESS; |
373 | | |
374 | 0 | case DEMUX_SET_POSITION: |
375 | 0 | f = va_arg( args, double ); |
376 | 0 | i64 = stream_Size( p_demux->s ); |
377 | |
|
378 | 0 | if( vlc_stream_Seek( p_demux->s, (int64_t)(i64 * f) ) || ReSynch( p_demux ) ) |
379 | 0 | return VLC_EGENERIC; |
380 | | |
381 | 0 | p_sys->i_time = -1; /* Invalidate time display */ |
382 | 0 | return VLC_SUCCESS; |
383 | | |
384 | 0 | case DEMUX_GET_TIME: |
385 | 0 | if( p_sys->i_time < 0 ) |
386 | 0 | { |
387 | 0 | *va_arg( args, vlc_tick_t * ) = 0; |
388 | 0 | return VLC_EGENERIC; |
389 | 0 | } |
390 | 0 | *va_arg( args, vlc_tick_t * ) = p_sys->i_time; |
391 | 0 | return VLC_SUCCESS; |
392 | | |
393 | | #if 0 |
394 | | case DEMUX_GET_LENGTH: |
395 | | if( p_sys->i_mux_rate > 0 ) |
396 | | { |
397 | | *va_arg( args, vlc_tick_t * ) = vlc_tick_from_samples( stream_Size( p_demux->s ) / 50, p_sys->i_mux_rate); |
398 | | return VLC_SUCCESS; |
399 | | } |
400 | | *va_arg( args, vlc_tick_t * ) = 0; |
401 | | return VLC_EGENERIC; |
402 | | |
403 | | #endif |
404 | 0 | case DEMUX_GET_FPS: |
405 | 0 | pf = va_arg( args, double * ); |
406 | 0 | *pf = (double)CLOCK_FREQ / (double)p_sys->i_pcr_inc; |
407 | 0 | return VLC_SUCCESS; |
408 | | |
409 | 0 | case DEMUX_CAN_RECORD: |
410 | 0 | pb_bool = va_arg( args, bool * ); |
411 | 0 | *pb_bool = true; |
412 | 0 | return VLC_SUCCESS; |
413 | | |
414 | 0 | case DEMUX_SET_RECORD_STATE: |
415 | 0 | { |
416 | 0 | b_bool = (bool)va_arg( args, int ); |
417 | 0 | const char *dir_path = va_arg( args, const char * ); |
418 | |
|
419 | 0 | free( p_sys->record_dir_path ); |
420 | 0 | p_sys->record_dir_path = NULL; |
421 | |
|
422 | 0 | if( !b_bool ) |
423 | 0 | vlc_stream_Control( p_demux->s, STREAM_SET_RECORD_STATE, false ); |
424 | 0 | else if( dir_path != NULL ) |
425 | 0 | { |
426 | 0 | p_sys->record_dir_path = strdup(dir_path); |
427 | 0 | if( p_sys->record_dir_path == NULL ) |
428 | 0 | return VLC_ENOMEM; |
429 | 0 | } |
430 | 0 | p_sys->b_start_record = b_bool; |
431 | 0 | return VLC_SUCCESS; |
432 | 0 | } |
433 | | |
434 | 0 | case DEMUX_SET_TIME: |
435 | 0 | return VLC_EGENERIC; |
436 | | |
437 | 0 | case DEMUX_CAN_PAUSE: |
438 | 0 | case DEMUX_SET_PAUSE_STATE: |
439 | 0 | case DEMUX_CAN_CONTROL_PACE: |
440 | 0 | case DEMUX_GET_PTS_DELAY: |
441 | 0 | return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args ); |
442 | | |
443 | 0 | default: |
444 | 0 | return VLC_EGENERIC; |
445 | 0 | } |
446 | 0 | } |
447 | | |
448 | | /***************************************************************************** |
449 | | * ReSynch: |
450 | | *****************************************************************************/ |
451 | | static int ReSynch( demux_t *p_demux ) |
452 | 0 | { |
453 | 0 | for( ;; ) |
454 | 0 | { |
455 | 0 | const uint8_t *p_peek; |
456 | 0 | int i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 1024 ); |
457 | 0 | if( i_peek < 8 ) |
458 | 0 | break; |
459 | | |
460 | 0 | int i_skip = 0; |
461 | |
|
462 | 0 | while( i_skip < i_peek - 4 ) |
463 | 0 | { |
464 | 0 | if( !memcmp( p_peek, "NSVf", 4 ) |
465 | 0 | || !memcmp( p_peek, "NSVs", 4 ) ) |
466 | 0 | { |
467 | 0 | if( i_skip > 0 && vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip ) |
468 | 0 | return VLC_EGENERIC; |
469 | 0 | return VLC_SUCCESS; |
470 | 0 | } |
471 | 0 | p_peek++; |
472 | 0 | i_skip++; |
473 | 0 | } |
474 | | |
475 | 0 | if( vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip ) |
476 | 0 | break; |
477 | 0 | } |
478 | 0 | return VLC_EGENERIC; |
479 | 0 | } |
480 | | |
481 | | /***************************************************************************** |
482 | | * ReadNSVf: |
483 | | *****************************************************************************/ |
484 | | static int ReadNSVf( demux_t *p_demux ) |
485 | 0 | { |
486 | | /* demux_sys_t *p_sys = p_demux->p_sys; */ |
487 | 0 | const uint8_t *p; |
488 | |
|
489 | 0 | msg_Dbg( p_demux, "new NSVf chunk" ); |
490 | 0 | if( vlc_stream_Peek( p_demux->s, &p, 8 ) < 8 ) |
491 | 0 | { |
492 | 0 | return VLC_EGENERIC; |
493 | 0 | } |
494 | | |
495 | 0 | uint32_t i_header_size = GetDWLE( &p[4] ); |
496 | 0 | msg_Dbg( p_demux, " - size=%" PRIu32, i_header_size ); |
497 | |
|
498 | 0 | if( i_header_size == 0 || i_header_size == UINT32_MAX ) |
499 | 0 | return VLC_EGENERIC; |
500 | | #if (UINT32_MAX > SSIZE_MAX) |
501 | | if( i_header_size > SSIZE_MAX ) |
502 | | return VLC_EGENERIC; |
503 | | #endif |
504 | 0 | if ( vlc_stream_Read( p_demux->s, NULL, i_header_size ) != i_header_size ) |
505 | 0 | return VLC_EGENERIC; |
506 | 0 | return VLC_SUCCESS; |
507 | 0 | } |
508 | | /***************************************************************************** |
509 | | * ReadNSVs: |
510 | | *****************************************************************************/ |
511 | | static int ReadNSVs( demux_t *p_demux ) |
512 | 0 | { |
513 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
514 | 0 | uint8_t header[19]; |
515 | 0 | vlc_fourcc_t fcc; |
516 | |
|
517 | 0 | if( vlc_stream_Read( p_demux->s, header, 19 ) < 19 ) |
518 | 0 | { |
519 | 0 | msg_Warn( p_demux, "cannot read" ); |
520 | 0 | return VLC_EGENERIC; |
521 | 0 | } |
522 | | |
523 | | /* Video */ |
524 | 0 | switch( ( fcc = VLC_FOURCC( header[4], header[5], header[6], header[7] ) ) ) |
525 | 0 | { |
526 | 0 | case VLC_FOURCC( 'V', 'P', '3', ' ' ): |
527 | 0 | case VLC_FOURCC( 'V', 'P', '3', '0' ): |
528 | 0 | fcc = VLC_FOURCC( 'V', 'P', '3', '0' ); |
529 | 0 | break; |
530 | | |
531 | 0 | case VLC_FOURCC( 'V', 'P', '3', '1' ): |
532 | 0 | fcc = VLC_FOURCC( 'V', 'P', '3', '1' ); |
533 | 0 | break; |
534 | | |
535 | 0 | case VLC_FOURCC( 'V', 'P', '5', ' ' ): |
536 | 0 | case VLC_FOURCC( 'V', 'P', '5', '0' ): |
537 | 0 | fcc = VLC_FOURCC( 'V', 'P', '5', '0' ); |
538 | 0 | break; |
539 | 0 | case VLC_FOURCC( 'V', 'P', '6', '0' ): |
540 | 0 | case VLC_FOURCC( 'V', 'P', '6', '1' ): |
541 | 0 | case VLC_FOURCC( 'V', 'P', '6', '2' ): |
542 | 0 | case VLC_FOURCC( 'V', 'P', '8', '0' ): |
543 | 0 | case VLC_FOURCC( 'H', '2', '6', '4' ): |
544 | 0 | case VLC_FOURCC( 'N', 'O', 'N', 'E' ): |
545 | 0 | break; |
546 | 0 | default: |
547 | 0 | msg_Err( p_demux, "unsupported video codec %4.4s", (char *)&fcc ); |
548 | 0 | break; |
549 | 0 | } |
550 | 0 | if( fcc != VLC_FOURCC( 'N', 'O', 'N', 'E' ) && fcc != p_sys->fmt_video.i_codec ) |
551 | 0 | { |
552 | 0 | es_format_Init( &p_sys->fmt_video, VIDEO_ES, fcc ); |
553 | 0 | p_sys->fmt_video.video.i_width = GetWLE( &header[12] ); |
554 | 0 | p_sys->fmt_video.video.i_height = GetWLE( &header[14] ); |
555 | 0 | p_sys->fmt_video.video.i_visible_width = p_sys->fmt_video.video.i_width; |
556 | 0 | p_sys->fmt_video.video.i_visible_height = p_sys->fmt_video.video.i_height; |
557 | 0 | if( p_sys->p_video ) |
558 | 0 | { |
559 | 0 | es_out_Del( p_demux->out, p_sys->p_video ); |
560 | 0 | } |
561 | 0 | p_sys->p_video = es_out_Add( p_demux->out, &p_sys->fmt_video ); |
562 | |
|
563 | 0 | msg_Dbg( p_demux, " - video `%4.4s' %dx%d", |
564 | 0 | (char*)&fcc, |
565 | 0 | p_sys->fmt_video.video.i_width, |
566 | 0 | p_sys->fmt_video.video.i_height ); |
567 | 0 | } |
568 | | |
569 | | /* Audio */ |
570 | 0 | switch( ( fcc = VLC_FOURCC( header[8], header[9], header[10], header[11] ) ) ) |
571 | 0 | { |
572 | 0 | case VLC_FOURCC( 'M', 'P', '3', ' ' ): |
573 | 0 | fcc = VLC_FOURCC( 'm', 'p', 'g', 'a' ); |
574 | 0 | break; |
575 | 0 | case VLC_FOURCC( 'P', 'C', 'M', ' ' ): |
576 | 0 | fcc = VLC_FOURCC( 'a', 'r', 'a', 'w' ); |
577 | 0 | break; |
578 | 0 | case VLC_FOURCC( 'A', 'A', 'C', ' ' ): |
579 | 0 | case VLC_FOURCC( 'A', 'A', 'C', 'P' ): |
580 | 0 | fcc = VLC_FOURCC( 'm', 'p', '4', 'a' ); |
581 | 0 | break; |
582 | 0 | case VLC_FOURCC( 'S', 'P', 'X', ' ' ): |
583 | 0 | fcc = VLC_FOURCC( 's', 'p', 'x', ' ' ); |
584 | 0 | break; |
585 | 0 | case VLC_FOURCC( 'N', 'O', 'N', 'E' ): |
586 | 0 | break; |
587 | 0 | default: |
588 | 0 | msg_Err( p_demux, "unsupported audio codec %4.4s", (char *)&fcc ); |
589 | 0 | break; |
590 | 0 | } |
591 | | |
592 | 0 | if( fcc != VLC_FOURCC( 'N', 'O', 'N', 'E' ) && fcc != p_sys->fmt_audio.i_codec ) |
593 | 0 | { |
594 | 0 | msg_Dbg( p_demux, " - audio `%4.4s'", (char*)&fcc ); |
595 | |
|
596 | 0 | if( p_sys->p_audio ) |
597 | 0 | { |
598 | 0 | es_out_Del( p_demux->out, p_sys->p_audio ); |
599 | 0 | p_sys->p_audio = NULL; |
600 | 0 | } |
601 | 0 | es_format_Init( &p_sys->fmt_audio, AUDIO_ES, fcc ); |
602 | 0 | } |
603 | |
|
604 | 0 | if( header[16]&0x80 ) |
605 | 0 | { |
606 | | /* Fractional frame rate */ |
607 | 0 | switch( header[16]&0x03 ) |
608 | 0 | { |
609 | 0 | case 0: /* 30 fps */ |
610 | 0 | p_sys->i_pcr_inc = 33333; /* 300000/9 */ |
611 | 0 | break; |
612 | 0 | case 1: /* 29.97 fps */ |
613 | 0 | p_sys->i_pcr_inc = 33367; /* 300300/9 */ |
614 | 0 | break; |
615 | 0 | case 2: /* 25 fps */ |
616 | 0 | p_sys->i_pcr_inc = 40000; /* 360000/9 */ |
617 | 0 | break; |
618 | 0 | case 3: /* 23.98 fps */ |
619 | 0 | p_sys->i_pcr_inc = 41700; /* 375300/9 */ |
620 | 0 | break; |
621 | 0 | } |
622 | | |
623 | 0 | if( header[16] < 0xc0 ) |
624 | 0 | p_sys->i_pcr_inc = p_sys->i_pcr_inc * (((header[16] ^ 0x80) >> 2 ) +1 ); |
625 | 0 | else |
626 | 0 | p_sys->i_pcr_inc = p_sys->i_pcr_inc / (((header[16] ^ 0xc0) >> 2 ) +1 ); |
627 | 0 | } |
628 | 0 | else if( header[16] != 0 ) |
629 | 0 | { |
630 | | /* Integer frame rate */ |
631 | 0 | p_sys->i_pcr_inc = vlc_tick_from_samples(1, header[16]); |
632 | 0 | } |
633 | 0 | else |
634 | 0 | { |
635 | 0 | msg_Dbg( p_demux, "invalid fps (0x00)" ); |
636 | 0 | p_sys->i_pcr_inc = VLC_TICK_FROM_MS(40); |
637 | 0 | } |
638 | | //msg_Dbg( p_demux, " - fps=%.3f", 1000000.0 / (double)p_sys->i_pcr_inc ); |
639 | | |
640 | 0 | if( p_sys->p_audio == NULL && p_sys->p_video == NULL ) |
641 | 0 | { |
642 | 0 | msg_Err( p_demux, "unable to play neither audio nor video, aborting." ); |
643 | 0 | return VLC_EGENERIC; |
644 | 0 | } |
645 | | |
646 | 0 | return VLC_SUCCESS; |
647 | 0 | } |
648 | | |