/src/vlc/modules/demux/mpeg/ps.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * ps.c: Program Stream demux module for VLC. |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2004-2009 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 "pes.h" |
36 | | #include "ps.h" |
37 | | |
38 | | /* TODO: |
39 | | * - re-add pre-scanning. |
40 | | * - ... |
41 | | */ |
42 | | |
43 | | #define TIME_TEXT N_("Trust MPEG timestamps") |
44 | | #define TIME_LONGTEXT N_("Normally we use the timestamps of the MPEG files " \ |
45 | | "to calculate position and duration. However sometimes this might not " \ |
46 | | "be usable. Disable this option to calculate from the bitrate instead." ) |
47 | | |
48 | 0 | #define PS_PACKET_PROBE 3 |
49 | 0 | #define CDXA_HEADER_SIZE 44 |
50 | 0 | #define CDXA_SECTOR_SIZE 2352 |
51 | 0 | #define CDXA_SECTOR_HEADER_SIZE 24 |
52 | | |
53 | | /***************************************************************************** |
54 | | * Module descriptor |
55 | | *****************************************************************************/ |
56 | | static int OpenForce( vlc_object_t * ); |
57 | | static int Open ( vlc_object_t * ); |
58 | | static void Close ( vlc_object_t * ); |
59 | | |
60 | 4 | vlc_module_begin () |
61 | 2 | set_description( N_("MPEG-PS demuxer") ) |
62 | 2 | set_shortname( N_("PS") ) |
63 | 2 | set_subcategory( SUBCAT_INPUT_DEMUX ) |
64 | 2 | set_capability( "demux", 1 ) |
65 | 4 | set_callbacks( OpenForce, Close ) |
66 | 2 | add_shortcut( "ps" ) |
67 | | |
68 | 2 | add_bool( "ps-trust-timestamps", true, TIME_TEXT, |
69 | 2 | TIME_LONGTEXT ) |
70 | 2 | change_safe () |
71 | | |
72 | 2 | add_submodule () |
73 | 2 | set_description( N_("MPEG-PS demuxer") ) |
74 | 2 | set_capability( "demux", 9 ) |
75 | 4 | set_callbacks( Open, Close ) |
76 | 2 | add_shortcut( "ps" ) |
77 | 2 | vlc_module_end () |
78 | | |
79 | | /***************************************************************************** |
80 | | * Local prototypes |
81 | | *****************************************************************************/ |
82 | | |
83 | | typedef struct |
84 | | { |
85 | | ps_psm_t psm; |
86 | | ps_track_t tk[PS_TK_COUNT]; |
87 | | |
88 | | vlc_tick_t i_pack_scr; /* current read pack scr value, temp */ |
89 | | vlc_tick_t i_first_scr; /* media offset */ |
90 | | vlc_tick_t i_scr; /* committed, current position */ |
91 | | int64_t i_scr_track_id; |
92 | | int i_mux_rate; |
93 | | vlc_tick_t i_length; |
94 | | int i_time_track_index; |
95 | | vlc_tick_t i_current_pts; |
96 | | uint64_t i_start_byte; |
97 | | uint64_t i_lastpack_byte; |
98 | | |
99 | | int i_aob_mlp_count; |
100 | | |
101 | | bool b_lost_sync; |
102 | | bool b_have_pack; |
103 | | bool b_bad_scr; |
104 | | bool b_seekable; |
105 | | enum |
106 | | { |
107 | | MPEG_PS = 0, |
108 | | CDXA_PS, |
109 | | PSMF_PS, |
110 | | IMKH_PS, |
111 | | } format; |
112 | | |
113 | | int current_title; |
114 | | int current_seekpoint; |
115 | | unsigned updates; |
116 | | } demux_sys_t; |
117 | | |
118 | | static int Demux ( demux_t *p_demux ); |
119 | | static int Control( demux_t *p_demux, int i_query, va_list args ); |
120 | | |
121 | | static int ps_pkt_resynch( stream_t *, int, bool ); |
122 | | static block_t *ps_pkt_read ( stream_t * ); |
123 | | |
124 | | static void CreateOrUpdateES( demux_t*p_demux ) |
125 | 0 | { |
126 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
127 | |
|
128 | 0 | for( int i = 0; i < PS_TK_COUNT; i++ ) |
129 | 0 | { |
130 | 0 | ps_track_t *tk = &p_sys->tk[i]; |
131 | 0 | if( !tk->b_updated ) |
132 | 0 | continue; |
133 | | |
134 | 0 | if( tk->es ) |
135 | 0 | es_out_Del( p_demux->out, tk->es ); |
136 | |
|
137 | 0 | if( tk->fmt.i_cat != UNKNOWN_ES ) |
138 | 0 | tk->es = es_out_Add( p_demux->out, &tk->fmt ); |
139 | 0 | tk->b_updated = false; |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | /***************************************************************************** |
144 | | * Open |
145 | | *****************************************************************************/ |
146 | | static int OpenCommon( vlc_object_t *p_this, bool b_force ) |
147 | 0 | { |
148 | 0 | demux_t *p_demux = (demux_t*)p_this; |
149 | 0 | demux_sys_t *p_sys; |
150 | |
|
151 | 0 | const uint8_t *p_peek; |
152 | 0 | ssize_t i_peek = 0; |
153 | 0 | ssize_t i_offset = 0; |
154 | 0 | int i_skip = 0; |
155 | 0 | unsigned i_max_packets = PS_PACKET_PROBE; |
156 | 0 | int format = MPEG_PS; |
157 | 0 | int i_mux_rate = 0; |
158 | 0 | vlc_tick_t i_length = VLC_TICK_INVALID; |
159 | |
|
160 | 0 | i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 16 ); |
161 | 0 | if( i_peek < 16 ) |
162 | 0 | return VLC_EGENERIC; |
163 | | |
164 | 0 | if( !memcmp( p_peek, "PSMF", 4 ) && |
165 | 0 | (GetDWBE( &p_peek[4] ) & 0x30303030) == 0x30303030 ) |
166 | 0 | { |
167 | 0 | i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 100 ); |
168 | 0 | if( i_peek < 100 ) |
169 | 0 | return VLC_EGENERIC; |
170 | 0 | i_skip = i_offset = GetWBE( &p_peek[10] ); |
171 | 0 | format = PSMF_PS; |
172 | 0 | msg_Info( p_demux, "Detected PSMF-PS header"); |
173 | 0 | i_mux_rate = GetDWBE( &p_peek[96] ); |
174 | 0 | if( GetDWBE( &p_peek[86] ) > 0 ) |
175 | 0 | i_length = vlc_tick_from_samples( GetDWBE( &p_peek[92] ), GetDWBE( &p_peek[86] )); |
176 | 0 | } |
177 | 0 | else if( !memcmp( p_peek, "IMKH", 4 ) ) |
178 | 0 | { |
179 | 0 | msg_Info( p_demux, "Detected Hikvision PVA header"); |
180 | 0 | i_skip = 40; |
181 | 0 | format = IMKH_PS; |
182 | 0 | i_max_packets = 0; |
183 | 0 | } |
184 | 0 | else if( !memcmp( p_peek, "RIFF", 4 ) && !memcmp( &p_peek[8], "CDXA", 4 ) ) |
185 | 0 | { |
186 | 0 | format = CDXA_PS; |
187 | 0 | i_max_packets = 0; /* We can't probe here */ |
188 | 0 | i_skip = CDXA_HEADER_SIZE; |
189 | 0 | msg_Info( p_demux, "Detected CDXA-PS" ); |
190 | | /* FIXME: have a proper way to decap CD sectors or make an access stream filter */ |
191 | 0 | } |
192 | 0 | else if( b_force ) |
193 | 0 | { |
194 | 0 | msg_Warn( p_demux, "this does not look like an MPEG PS stream, " |
195 | 0 | "continuing anyway" ); |
196 | 0 | i_max_packets = 0; |
197 | 0 | } |
198 | | |
199 | 0 | for( unsigned i=0; i<i_max_packets; i++ ) |
200 | 0 | { |
201 | 0 | if( i_peek < i_offset + 16 ) |
202 | 0 | { |
203 | 0 | i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_offset + 16 ); |
204 | 0 | if( i_peek < i_offset + 16 ) |
205 | 0 | return VLC_EGENERIC; |
206 | 0 | } |
207 | | |
208 | 0 | const uint8_t startcode[3] = { 0x00, 0x00, 0x01 }; |
209 | 0 | const uint8_t *p_header = &p_peek[i_offset]; |
210 | 0 | if( memcmp( p_header, startcode, 3 ) || |
211 | 0 | ( (p_header[3] & 0xB0) != 0xB0 && |
212 | 0 | !(p_header[3] >= 0xC0 && p_header[3] <= 0xEF) && |
213 | 0 | p_header[3] != STREAM_ID_EXTENDED_STREAM_ID && |
214 | 0 | p_header[3] != STREAM_ID_PROGRAM_STREAM_DIRECTORY ) ) |
215 | 0 | return VLC_EGENERIC; |
216 | | |
217 | 0 | ssize_t i_pessize = ps_pkt_size( p_header, 16 ); |
218 | 0 | if( i_pessize < 5 ) |
219 | 0 | return VLC_EGENERIC; |
220 | 0 | i_offset += i_pessize; |
221 | 0 | } |
222 | | |
223 | 0 | if( i_skip && !p_demux->b_preparsing && |
224 | 0 | vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip ) |
225 | 0 | return VLC_EGENERIC; |
226 | | |
227 | | /* Fill p_demux field */ |
228 | 0 | p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); |
229 | 0 | if( !p_sys ) return VLC_ENOMEM; |
230 | | |
231 | 0 | p_demux->pf_demux = Demux; |
232 | 0 | p_demux->pf_control = Control; |
233 | | |
234 | | /* Init p_sys */ |
235 | 0 | p_sys->i_mux_rate = i_mux_rate; |
236 | 0 | p_sys->i_pack_scr = VLC_TICK_INVALID; |
237 | 0 | p_sys->i_first_scr = VLC_TICK_INVALID; |
238 | 0 | p_sys->i_scr = VLC_TICK_INVALID; |
239 | 0 | p_sys->i_scr_track_id = 0; |
240 | 0 | p_sys->i_length = i_length; |
241 | 0 | p_sys->i_current_pts = VLC_TICK_INVALID; |
242 | 0 | p_sys->i_time_track_index = -1; |
243 | 0 | p_sys->i_aob_mlp_count = 0; |
244 | 0 | p_sys->i_start_byte = i_skip; |
245 | 0 | p_sys->i_lastpack_byte = i_skip; |
246 | |
|
247 | 0 | p_sys->b_lost_sync = false; |
248 | 0 | p_sys->b_have_pack = false; |
249 | 0 | p_sys->b_bad_scr = false; |
250 | 0 | p_sys->b_seekable = false; |
251 | 0 | p_sys->format = format; |
252 | 0 | p_sys->current_title = 0; |
253 | 0 | p_sys->current_seekpoint = 0; |
254 | 0 | p_sys->updates = 0; |
255 | |
|
256 | 0 | vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable ); |
257 | |
|
258 | 0 | ps_psm_init( &p_sys->psm ); |
259 | 0 | ps_track_init( p_sys->tk ); |
260 | | |
261 | | /* TODO prescanning of ES */ |
262 | |
|
263 | 0 | return VLC_SUCCESS; |
264 | 0 | } |
265 | | |
266 | | static int OpenForce( vlc_object_t *p_this ) |
267 | 0 | { |
268 | 0 | return OpenCommon( p_this, true ); |
269 | 0 | } |
270 | | |
271 | | static int Open( vlc_object_t *p_this ) |
272 | 0 | { |
273 | 0 | return OpenCommon( p_this, p_this->force ); |
274 | 0 | } |
275 | | |
276 | | /***************************************************************************** |
277 | | * Close |
278 | | *****************************************************************************/ |
279 | | static void Close( vlc_object_t *p_this ) |
280 | 0 | { |
281 | 0 | demux_t *p_demux = (demux_t*)p_this; |
282 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
283 | 0 | int i; |
284 | |
|
285 | 0 | for( i = 0; i < PS_TK_COUNT; i++ ) |
286 | 0 | { |
287 | 0 | ps_track_t *tk = &p_sys->tk[i]; |
288 | 0 | if( tk->b_configured ) |
289 | 0 | { |
290 | 0 | es_format_Clean( &tk->fmt ); |
291 | 0 | if( tk->es ) es_out_Del( p_demux->out, tk->es ); |
292 | 0 | } |
293 | 0 | } |
294 | |
|
295 | 0 | ps_psm_destroy( &p_sys->psm ); |
296 | |
|
297 | 0 | free( p_sys ); |
298 | 0 | } |
299 | | |
300 | | static int Probe( demux_t *p_demux, bool b_end ) |
301 | 0 | { |
302 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
303 | 0 | int i_ret, i_id; |
304 | 0 | block_t *p_pkt; |
305 | |
|
306 | 0 | i_ret = ps_pkt_resynch( p_demux->s, p_sys->format, p_sys->b_have_pack ); |
307 | 0 | if( i_ret < 0 ) |
308 | 0 | { |
309 | 0 | return VLC_DEMUXER_EOF; |
310 | 0 | } |
311 | 0 | else if( i_ret == 0 ) |
312 | 0 | { |
313 | 0 | if( !p_sys->b_lost_sync ) |
314 | 0 | msg_Warn( p_demux, "garbage at input, trying to resync..." ); |
315 | |
|
316 | 0 | p_sys->b_lost_sync = true; |
317 | 0 | return VLC_DEMUXER_SUCCESS; |
318 | 0 | } |
319 | | |
320 | 0 | if( p_sys->b_lost_sync ) msg_Warn( p_demux, "found sync code" ); |
321 | 0 | p_sys->b_lost_sync = false; |
322 | |
|
323 | 0 | if( ( p_pkt = ps_pkt_read( p_demux->s ) ) == NULL ) |
324 | 0 | { |
325 | 0 | return VLC_DEMUXER_EOF; |
326 | 0 | } |
327 | | |
328 | 0 | i_id = ps_pkt_id( p_pkt->p_buffer, p_pkt->i_buffer ); |
329 | 0 | if( i_id >= 0xc0 ) |
330 | 0 | { |
331 | 0 | ps_track_t *tk = &p_sys->tk[ps_id_to_tk(i_id)]; |
332 | 0 | if( !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) && |
333 | 0 | p_pkt->i_pts != VLC_TICK_INVALID ) |
334 | 0 | { |
335 | 0 | if( b_end && (tk->i_last_pts == VLC_TICK_INVALID || p_pkt->i_pts > tk->i_last_pts) ) |
336 | 0 | { |
337 | 0 | tk->i_last_pts = p_pkt->i_pts; |
338 | 0 | } |
339 | 0 | else if ( tk->i_first_pts == VLC_TICK_INVALID ) |
340 | 0 | { |
341 | 0 | tk->i_first_pts = p_pkt->i_pts; |
342 | 0 | } |
343 | 0 | } |
344 | 0 | } |
345 | 0 | else if( i_id == PS_STREAM_ID_PACK_HEADER ) |
346 | 0 | { |
347 | 0 | vlc_tick_t i_scr; int dummy; |
348 | 0 | if( !b_end && !ps_pkt_parse_pack( p_pkt->p_buffer, p_pkt->i_buffer, |
349 | 0 | &i_scr, &dummy ) ) |
350 | 0 | { |
351 | 0 | if( p_sys->i_first_scr == VLC_TICK_INVALID ) |
352 | 0 | p_sys->i_first_scr = i_scr; |
353 | 0 | } |
354 | 0 | p_sys->b_have_pack = true; |
355 | 0 | } |
356 | |
|
357 | 0 | block_Release( p_pkt ); |
358 | 0 | return VLC_DEMUXER_SUCCESS; |
359 | 0 | } |
360 | | |
361 | | static bool FindLength( demux_t *p_demux ) |
362 | 0 | { |
363 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
364 | 0 | int64_t i_current_pos = -1, i_size = 0, i_end = 0; |
365 | |
|
366 | 0 | if( !var_CreateGetBool( p_demux, "ps-trust-timestamps" ) ) |
367 | 0 | return true; |
368 | | |
369 | 0 | if( p_sys->i_length == VLC_TICK_INVALID ) /* First time */ |
370 | 0 | { |
371 | 0 | p_sys->i_length = VLC_TICK_0; |
372 | | /* Check beginning */ |
373 | 0 | int i = 0; |
374 | 0 | i_current_pos = vlc_stream_Tell( p_demux->s ); |
375 | 0 | while( i < 40 && Probe( p_demux, false ) > 0 ) i++; |
376 | | |
377 | | /* Check end */ |
378 | 0 | i_size = stream_Size( p_demux->s ); |
379 | 0 | i_end = VLC_CLIP( i_size, 0, 200000 ); |
380 | 0 | if( vlc_stream_Seek( p_demux->s, i_size - i_end ) == VLC_SUCCESS ) |
381 | 0 | { |
382 | 0 | i = 0; |
383 | 0 | while( i < 400 && Probe( p_demux, true ) > 0 ) i++; |
384 | 0 | if( i_current_pos >= 0 && |
385 | 0 | vlc_stream_Seek( p_demux->s, i_current_pos ) != VLC_SUCCESS ) |
386 | 0 | return false; |
387 | 0 | } |
388 | 0 | else return false; |
389 | 0 | } |
390 | | |
391 | | /* Find the longest track */ |
392 | 0 | for( int i = 0; i < PS_TK_COUNT; i++ ) |
393 | 0 | { |
394 | 0 | ps_track_t *tk = &p_sys->tk[i]; |
395 | 0 | if( tk->i_first_pts != VLC_TICK_INVALID && |
396 | 0 | tk->i_last_pts > tk->i_first_pts ) |
397 | 0 | { |
398 | 0 | vlc_tick_t i_length = tk->i_last_pts - tk->i_first_pts; |
399 | 0 | if( i_length > p_sys->i_length ) |
400 | 0 | { |
401 | 0 | p_sys->i_length = i_length; |
402 | 0 | p_sys->i_time_track_index = i; |
403 | 0 | msg_Dbg( p_demux, "we found a length of: %"PRId64 "s", SEC_FROM_VLC_TICK(p_sys->i_length) ); |
404 | 0 | } |
405 | 0 | } |
406 | 0 | } |
407 | 0 | return true; |
408 | 0 | } |
409 | | |
410 | | static void NotifyDiscontinuity( ps_track_t *p_tk, es_out_t *out ) |
411 | 0 | { |
412 | 0 | bool b_selected; |
413 | 0 | for( size_t i = 0; i < PS_TK_COUNT; i++ ) |
414 | 0 | { |
415 | 0 | ps_track_t *tk = &p_tk[i]; |
416 | 0 | if( tk->es && |
417 | 0 | es_out_Control( out, ES_OUT_GET_ES_STATE, tk->es, &b_selected ) == VLC_SUCCESS |
418 | 0 | && b_selected ) |
419 | 0 | { |
420 | 0 | tk->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY; |
421 | 0 | } |
422 | 0 | } |
423 | 0 | } |
424 | | |
425 | | static void CheckPCR( demux_sys_t *p_sys, es_out_t *out, vlc_tick_t i_scr ) |
426 | 0 | { |
427 | 0 | if( p_sys->i_scr != VLC_TICK_INVALID && |
428 | 0 | llabs( p_sys->i_scr - i_scr ) > VLC_TICK_FROM_SEC(1) ) |
429 | 0 | NotifyDiscontinuity( p_sys->tk, out ); |
430 | 0 | } |
431 | | |
432 | | /***************************************************************************** |
433 | | * Demux: |
434 | | *****************************************************************************/ |
435 | | static int Demux( demux_t *p_demux ) |
436 | 0 | { |
437 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
438 | 0 | int i_ret, i_mux_rate; |
439 | 0 | block_t *p_pkt; |
440 | |
|
441 | 0 | i_ret = ps_pkt_resynch( p_demux->s, p_sys->format, p_sys->b_have_pack ); |
442 | 0 | if( i_ret < 0 ) |
443 | 0 | { |
444 | 0 | return VLC_DEMUXER_EOF; |
445 | 0 | } |
446 | 0 | else if( i_ret == 0 ) |
447 | 0 | { |
448 | 0 | if( !p_sys->b_lost_sync ) |
449 | 0 | { |
450 | 0 | msg_Warn( p_demux, "garbage at input from %"PRIu64", trying to resync...", |
451 | 0 | vlc_stream_Tell(p_demux->s) ); |
452 | 0 | NotifyDiscontinuity( p_sys->tk, p_demux->out ); |
453 | 0 | } |
454 | |
|
455 | 0 | p_sys->b_lost_sync = true; |
456 | 0 | return VLC_DEMUXER_SUCCESS; |
457 | 0 | } |
458 | | |
459 | 0 | if( p_sys->b_lost_sync ) msg_Warn( p_demux, "found sync code" ); |
460 | 0 | p_sys->b_lost_sync = false; |
461 | |
|
462 | 0 | if( p_sys->i_length == VLC_TICK_INVALID && p_sys->b_seekable ) |
463 | 0 | { |
464 | 0 | if( !FindLength( p_demux ) ) |
465 | 0 | return VLC_DEMUXER_EGENERIC; |
466 | 0 | } |
467 | | |
468 | 0 | if( ( p_pkt = ps_pkt_read( p_demux->s ) ) == NULL ) |
469 | 0 | { |
470 | 0 | return VLC_DEMUXER_EOF; |
471 | 0 | } |
472 | | |
473 | 0 | if( p_pkt->i_buffer < 4 ) |
474 | 0 | { |
475 | 0 | block_Release( p_pkt ); |
476 | 0 | return VLC_DEMUXER_EGENERIC; |
477 | 0 | } |
478 | | |
479 | 0 | const uint8_t i_stream_id = p_pkt->p_buffer[3]; |
480 | 0 | switch( i_stream_id ) |
481 | 0 | { |
482 | 0 | case PS_STREAM_ID_END_STREAM: |
483 | 0 | case STREAM_ID_PADDING: |
484 | 0 | block_Release( p_pkt ); |
485 | 0 | break; |
486 | | |
487 | 0 | case PS_STREAM_ID_PACK_HEADER: |
488 | 0 | if( !ps_pkt_parse_pack( p_pkt->p_buffer, p_pkt->i_buffer, |
489 | 0 | &p_sys->i_pack_scr, &i_mux_rate ) ) |
490 | 0 | { |
491 | 0 | if( p_sys->i_first_scr == VLC_TICK_INVALID ) |
492 | 0 | p_sys->i_first_scr = p_sys->i_pack_scr; |
493 | 0 | CheckPCR( p_sys, p_demux->out, p_sys->i_pack_scr ); |
494 | 0 | p_sys->i_scr = p_sys->i_pack_scr; |
495 | 0 | p_sys->i_lastpack_byte = vlc_stream_Tell( p_demux->s ); |
496 | 0 | if( !p_sys->b_have_pack ) p_sys->b_have_pack = true; |
497 | | /* done later on to work around bad vcd/svcd streams */ |
498 | | /* es_out_SetPCR( p_demux->out, p_sys->i_scr ); */ |
499 | 0 | if( i_mux_rate > 0 ) p_sys->i_mux_rate = i_mux_rate; |
500 | 0 | } |
501 | 0 | block_Release( p_pkt ); |
502 | 0 | break; |
503 | | |
504 | 0 | case PS_STREAM_ID_SYSTEM_HEADER: |
505 | 0 | ps_pkt_parse_system( p_pkt->p_buffer, p_pkt->i_buffer, |
506 | 0 | &p_sys->psm, p_sys->tk ); |
507 | 0 | block_Release( p_pkt ); |
508 | 0 | break; |
509 | | |
510 | 0 | case STREAM_ID_PROGRAM_STREAM_MAP: |
511 | 0 | if( p_sys->psm.i_version == 0xFF ) |
512 | 0 | msg_Dbg( p_demux, "contains a PSM"); |
513 | |
|
514 | 0 | ps_psm_fill( &p_sys->psm, |
515 | 0 | p_pkt->p_buffer, p_pkt->i_buffer, |
516 | 0 | p_sys->tk ); |
517 | 0 | block_Release( p_pkt ); |
518 | 0 | break; |
519 | | |
520 | 0 | default: |
521 | | /* Reject non video/audio nor PES */ |
522 | 0 | if( i_stream_id < 0xC0 || i_stream_id > 0xEF ) |
523 | 0 | { |
524 | 0 | block_Release( p_pkt ); |
525 | 0 | break; |
526 | 0 | } |
527 | | /* fallthrough */ |
528 | 0 | case STREAM_ID_PRIVATE_STREAM_1: |
529 | 0 | case STREAM_ID_EXTENDED_STREAM_ID: |
530 | 0 | { |
531 | 0 | int i_id = ps_pkt_id( p_pkt->p_buffer, p_pkt->i_buffer ); |
532 | | /* Small heuristic to improve MLP detection from AOB */ |
533 | 0 | if( i_id == 0xa001 && |
534 | 0 | p_sys->i_aob_mlp_count < 500 ) |
535 | 0 | { |
536 | 0 | p_sys->i_aob_mlp_count++; |
537 | 0 | } |
538 | 0 | else if( i_id == 0xbda1 && |
539 | 0 | p_sys->i_aob_mlp_count > 0 ) |
540 | 0 | { |
541 | 0 | p_sys->i_aob_mlp_count--; |
542 | 0 | i_id = 0xa001; |
543 | 0 | } |
544 | |
|
545 | 0 | bool b_new = false; |
546 | 0 | ps_track_t *tk = &p_sys->tk[ps_id_to_tk(i_id)]; |
547 | |
|
548 | 0 | if( !tk->b_configured ) |
549 | 0 | { |
550 | 0 | if( !ps_track_fill( tk, &p_sys->psm, i_id, |
551 | 0 | p_pkt->p_buffer, p_pkt->i_buffer, false ) ) |
552 | 0 | { |
553 | 0 | if( p_sys->format == IMKH_PS ) |
554 | 0 | { |
555 | 0 | if( ps_id_to_type( &p_sys->psm , i_id ) == 0x91 ) |
556 | 0 | { |
557 | 0 | tk->fmt.i_codec = VLC_CODEC_MULAW; |
558 | 0 | tk->fmt.audio.i_channels = 1; |
559 | 0 | tk->fmt.audio.i_rate = 8000; |
560 | 0 | } |
561 | 0 | } |
562 | 0 | else |
563 | | /* No PSM and no probing yet */ |
564 | 0 | if( p_sys->format == PSMF_PS ) |
565 | 0 | { |
566 | 0 | if( tk->fmt.i_cat == VIDEO_ES ) |
567 | 0 | tk->fmt.i_codec = VLC_CODEC_H264; |
568 | | #if 0 |
569 | | if( i_stream_id == PS_STREAM_ID_PRIVATE_STREAM1 ) |
570 | | { |
571 | | es_format_Change( &tk->fmt, AUDIO_ES, VLC_CODEC_ATRAC3P ); |
572 | | tk->fmt.audio.i_blockalign = 376; |
573 | | tk->fmt.audio.i_channels = 2; |
574 | | tk->fmt.audio.i_rate = 44100; |
575 | | } |
576 | | #endif |
577 | 0 | } |
578 | |
|
579 | 0 | b_new = true; |
580 | 0 | tk->b_configured = true; |
581 | 0 | tk->b_updated = true; |
582 | 0 | } |
583 | 0 | else |
584 | 0 | { |
585 | 0 | msg_Dbg( p_demux, "es id=0x%x format unknown", i_id ); |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | /* Create all configured/updated ES when seeing a track packet */ |
590 | 0 | if( tk->b_updated ) |
591 | 0 | CreateOrUpdateES( p_demux ); |
592 | | |
593 | | /* The popular VCD/SVCD subtitling WinSubMux does not |
594 | | * renumber the SCRs when merging subtitles into the PES */ |
595 | 0 | if( !p_sys->b_bad_scr && p_sys->format == MPEG_PS && |
596 | 0 | ( tk->fmt.i_codec == VLC_CODEC_OGT || |
597 | 0 | tk->fmt.i_codec == VLC_CODEC_CVD ) ) |
598 | 0 | { |
599 | 0 | p_sys->b_bad_scr = true; |
600 | 0 | p_sys->i_first_scr = VLC_TICK_INVALID; |
601 | 0 | } |
602 | |
|
603 | 0 | if( p_sys->i_pack_scr != VLC_TICK_INVALID && !p_sys->b_bad_scr ) |
604 | 0 | { |
605 | 0 | if( (tk->fmt.i_cat == AUDIO_ES || tk->fmt.i_cat == VIDEO_ES) && |
606 | 0 | tk->i_first_pts != VLC_TICK_INVALID && tk->i_first_pts - p_sys->i_pack_scr > VLC_TICK_FROM_SEC(2)) |
607 | 0 | { |
608 | 0 | msg_Warn( p_demux, "Incorrect SCR timing offset by of %"PRId64 "ms, disabling", |
609 | 0 | MS_FROM_VLC_TICK(tk->i_first_pts - p_sys->i_pack_scr) ); |
610 | 0 | p_sys->b_bad_scr = true; /* Disable Offset SCR */ |
611 | 0 | p_sys->i_first_scr = VLC_TICK_INVALID; |
612 | 0 | } |
613 | 0 | else |
614 | 0 | es_out_SetPCR( p_demux->out, p_sys->i_pack_scr ); |
615 | 0 | } |
616 | |
|
617 | 0 | if( tk->b_configured && tk->es && |
618 | 0 | !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) ) |
619 | 0 | { |
620 | 0 | if( tk->fmt.i_cat == AUDIO_ES || tk->fmt.i_cat == VIDEO_ES ) |
621 | 0 | { |
622 | 0 | if( !p_sys->b_bad_scr && p_sys->i_pack_scr != VLC_TICK_INVALID && p_pkt->i_pts != VLC_TICK_INVALID && |
623 | 0 | p_sys->i_pack_scr > p_pkt->i_pts + VLC_TICK_FROM_MS(250) ) |
624 | 0 | { |
625 | 0 | msg_Warn( p_demux, "Incorrect SCR timing in advance of %" PRId64 "ms, disabling", |
626 | 0 | MS_FROM_VLC_TICK(p_sys->i_pack_scr - p_pkt->i_pts) ); |
627 | 0 | p_sys->b_bad_scr = true; |
628 | 0 | p_sys->i_first_scr = VLC_TICK_INVALID; |
629 | 0 | } |
630 | |
|
631 | 0 | if( (p_sys->b_bad_scr || !p_sys->b_have_pack) && !p_sys->i_scr_track_id ) |
632 | 0 | { |
633 | 0 | p_sys->i_scr_track_id = tk->i_id; |
634 | 0 | } |
635 | 0 | } |
636 | |
|
637 | 0 | if( ((!b_new && !p_sys->b_have_pack) || p_sys->b_bad_scr) && |
638 | 0 | p_sys->i_scr_track_id == tk->i_id && |
639 | 0 | p_pkt->i_pts != VLC_TICK_INVALID ) |
640 | 0 | { |
641 | | /* A hack to sync the A/V on PES files. */ |
642 | 0 | msg_Dbg( p_demux, "force SCR: %"PRId64, p_pkt->i_pts ); |
643 | 0 | CheckPCR( p_sys, p_demux->out, p_pkt->i_pts ); |
644 | 0 | p_sys->i_scr = p_pkt->i_pts; |
645 | 0 | if( p_sys->i_first_scr == VLC_TICK_INVALID ) |
646 | 0 | p_sys->i_first_scr = p_sys->i_scr; |
647 | 0 | es_out_SetPCR( p_demux->out, p_pkt->i_pts ); |
648 | 0 | } |
649 | |
|
650 | 0 | if( p_sys->format == IMKH_PS ) |
651 | 0 | { |
652 | | /* Synchronization on NVR is always broken */ |
653 | 0 | if( llabs( p_pkt->i_dts - p_sys->i_scr ) > VLC_TICK_FROM_SEC(2) ) |
654 | 0 | { |
655 | 0 | p_pkt->i_pts = p_pkt->i_pts - p_pkt->i_dts; |
656 | 0 | p_pkt->i_dts = p_sys->i_scr; |
657 | 0 | p_pkt->i_pts += p_sys->i_scr; |
658 | 0 | } |
659 | 0 | } |
660 | |
|
661 | 0 | if( tk->fmt.i_codec == VLC_CODEC_TELETEXT && |
662 | 0 | p_pkt->i_pts == VLC_TICK_INVALID && p_sys->i_scr != VLC_TICK_INVALID ) |
663 | 0 | { |
664 | | /* Teletext may have missing PTS (ETSI EN 300 472 Annexe A) |
665 | | * In this case use the last SCR + 40ms */ |
666 | 0 | p_pkt->i_pts = p_sys->i_scr + VLC_TICK_FROM_MS(40); |
667 | 0 | } |
668 | |
|
669 | 0 | if( p_pkt->i_pts > p_sys->i_current_pts ) |
670 | 0 | { |
671 | 0 | p_sys->i_current_pts = p_pkt->i_pts; |
672 | 0 | } |
673 | |
|
674 | 0 | if( tk->i_next_block_flags ) |
675 | 0 | { |
676 | 0 | p_pkt->i_flags = tk->i_next_block_flags; |
677 | 0 | tk->i_next_block_flags = 0; |
678 | 0 | } |
679 | | #if 0 |
680 | | if( tk->fmt.i_codec == VLC_CODEC_ATRAC3P ) |
681 | | { |
682 | | p_pkt->p_buffer += 14; |
683 | | p_pkt->i_buffer -= 14; |
684 | | } |
685 | | #endif |
686 | 0 | es_out_Send( p_demux->out, tk->es, p_pkt ); |
687 | 0 | } |
688 | 0 | else |
689 | 0 | { |
690 | 0 | block_Release( p_pkt ); |
691 | 0 | } |
692 | |
|
693 | 0 | p_sys->i_pack_scr = VLC_TICK_INVALID; |
694 | 0 | } |
695 | 0 | break; |
696 | 0 | } |
697 | | |
698 | 0 | demux_UpdateTitleFromStream( p_demux ); |
699 | 0 | return VLC_DEMUXER_SUCCESS; |
700 | 0 | } |
701 | | |
702 | | /***************************************************************************** |
703 | | * Control: |
704 | | *****************************************************************************/ |
705 | | static int Control( demux_t *p_demux, int i_query, va_list args ) |
706 | 0 | { |
707 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
708 | 0 | double f, *pf; |
709 | 0 | int64_t i64; |
710 | 0 | int i_ret; |
711 | |
|
712 | 0 | switch( i_query ) |
713 | 0 | { |
714 | 0 | case DEMUX_CAN_SEEK: |
715 | 0 | *va_arg( args, bool * ) = p_sys->b_seekable; |
716 | 0 | return VLC_SUCCESS; |
717 | | |
718 | 0 | case DEMUX_GET_TITLE: |
719 | 0 | *va_arg( args, int * ) = p_sys->current_title; |
720 | 0 | return VLC_SUCCESS; |
721 | | |
722 | 0 | case DEMUX_GET_SEEKPOINT: |
723 | 0 | *va_arg( args, int * ) = p_sys->current_seekpoint; |
724 | 0 | return VLC_SUCCESS; |
725 | | |
726 | 0 | case DEMUX_GET_POSITION: |
727 | 0 | pf = va_arg( args, double * ); |
728 | 0 | i64 = stream_Size( p_demux->s ) - p_sys->i_start_byte; |
729 | 0 | if( i64 > 0 ) |
730 | 0 | { |
731 | 0 | double current = vlc_stream_Tell( p_demux->s ) - p_sys->i_start_byte; |
732 | 0 | *pf = current / (double)i64; |
733 | 0 | } |
734 | 0 | else |
735 | 0 | { |
736 | 0 | *pf = 0.0; |
737 | 0 | } |
738 | 0 | return VLC_SUCCESS; |
739 | | |
740 | 0 | case DEMUX_SET_POSITION: |
741 | 0 | f = va_arg( args, double ); |
742 | 0 | i64 = stream_Size( p_demux->s ) - p_sys->i_start_byte; |
743 | 0 | p_sys->i_current_pts = VLC_TICK_INVALID; |
744 | 0 | p_sys->i_scr = VLC_TICK_INVALID; |
745 | |
|
746 | 0 | if( p_sys->format == CDXA_PS ) |
747 | 0 | { |
748 | 0 | i64 = (int64_t)(i64 * f); /* Align to sector payload */ |
749 | 0 | i64 = p_sys->i_start_byte + i64 - (i64 % CDXA_SECTOR_SIZE) + CDXA_SECTOR_HEADER_SIZE; |
750 | 0 | } |
751 | 0 | else |
752 | 0 | { |
753 | 0 | i64 = p_sys->i_start_byte + (int64_t)(i64 * f); |
754 | 0 | } |
755 | |
|
756 | 0 | i_ret = vlc_stream_Seek( p_demux->s, i64 ); |
757 | 0 | if( i_ret == VLC_SUCCESS ) |
758 | 0 | { |
759 | 0 | NotifyDiscontinuity( p_sys->tk, p_demux->out ); |
760 | 0 | return i_ret; |
761 | 0 | } |
762 | 0 | break; |
763 | | |
764 | 0 | case DEMUX_GET_TIME: |
765 | 0 | if( p_sys->i_time_track_index >= 0 && p_sys->i_current_pts != VLC_TICK_INVALID ) |
766 | 0 | { |
767 | 0 | *va_arg( args, vlc_tick_t * ) = p_sys->i_current_pts - p_sys->tk[p_sys->i_time_track_index].i_first_pts; |
768 | 0 | return VLC_SUCCESS; |
769 | 0 | } |
770 | 0 | if( p_sys->i_first_scr != VLC_TICK_INVALID && p_sys->i_scr != VLC_TICK_INVALID ) |
771 | 0 | { |
772 | 0 | vlc_tick_t i_time = p_sys->i_scr - p_sys->i_first_scr; |
773 | | /* H.222 2.5.2.2 */ |
774 | 0 | if( p_sys->i_mux_rate > 0 && p_sys->b_have_pack ) |
775 | 0 | { |
776 | 0 | uint64_t i_offset = vlc_stream_Tell( p_demux->s ) - p_sys->i_lastpack_byte; |
777 | 0 | i_time += vlc_tick_from_samples(i_offset, p_sys->i_mux_rate * 50); |
778 | 0 | } |
779 | 0 | *va_arg( args, vlc_tick_t * ) = i_time; |
780 | 0 | return VLC_SUCCESS; |
781 | 0 | } |
782 | 0 | *va_arg( args, vlc_tick_t * ) = 0; |
783 | 0 | break; |
784 | | |
785 | 0 | case DEMUX_GET_LENGTH: |
786 | 0 | if( p_sys->i_length > VLC_TICK_0 ) |
787 | 0 | { |
788 | 0 | *va_arg( args, vlc_tick_t * ) = p_sys->i_length; |
789 | 0 | return VLC_SUCCESS; |
790 | 0 | } |
791 | 0 | else if( p_sys->i_mux_rate > 0 ) |
792 | 0 | { |
793 | 0 | *va_arg( args, vlc_tick_t * ) = vlc_tick_from_samples( stream_Size( p_demux->s ) - p_sys->i_start_byte / 50, |
794 | 0 | p_sys->i_mux_rate ); |
795 | 0 | return VLC_SUCCESS; |
796 | 0 | } |
797 | 0 | *va_arg( args, vlc_tick_t * ) = 0; |
798 | 0 | break; |
799 | | |
800 | 0 | case DEMUX_SET_TIME: |
801 | 0 | { |
802 | 0 | if( p_sys->i_time_track_index >= 0 && p_sys->i_current_pts != VLC_TICK_INVALID && |
803 | 0 | p_sys->i_length > VLC_TICK_0) |
804 | 0 | { |
805 | 0 | vlc_tick_t i_time = va_arg( args, vlc_tick_t ); |
806 | 0 | i_time -= p_sys->tk[p_sys->i_time_track_index].i_first_pts; |
807 | 0 | return demux_SetPosition( p_demux, (double) i_time / p_sys->i_length, false ); |
808 | 0 | } |
809 | 0 | break; |
810 | 0 | } |
811 | | |
812 | 0 | case DEMUX_GET_TITLE_INFO: |
813 | 0 | { |
814 | 0 | struct input_title_t ***v = va_arg( args, struct input_title_t*** ); |
815 | 0 | int *c = va_arg( args, int * ); |
816 | |
|
817 | 0 | *va_arg( args, int* ) = 0; /* Title offset */ |
818 | 0 | *va_arg( args, int* ) = 0; /* Chapter offset */ |
819 | 0 | return vlc_stream_Control( p_demux->s, STREAM_GET_TITLE_INFO, v, |
820 | 0 | c ); |
821 | 0 | } |
822 | | |
823 | 0 | case DEMUX_SET_TITLE: |
824 | 0 | return vlc_stream_vaControl( p_demux->s, STREAM_SET_TITLE, args ); |
825 | | |
826 | 0 | case DEMUX_SET_SEEKPOINT: |
827 | 0 | return vlc_stream_vaControl( p_demux->s, STREAM_SET_SEEKPOINT, |
828 | 0 | args ); |
829 | | |
830 | 0 | case DEMUX_TEST_AND_CLEAR_FLAGS: |
831 | 0 | { |
832 | 0 | unsigned *restrict flags = va_arg(args, unsigned *); |
833 | 0 | *flags &= p_sys->updates; |
834 | 0 | p_sys->updates &= ~*flags; |
835 | 0 | return VLC_SUCCESS; |
836 | 0 | } |
837 | | |
838 | 0 | case DEMUX_GET_META: |
839 | 0 | return vlc_stream_vaControl( p_demux->s, STREAM_GET_META, args ); |
840 | | |
841 | 0 | case DEMUX_GET_FPS: |
842 | 0 | break; |
843 | | |
844 | 0 | case DEMUX_CAN_PAUSE: |
845 | 0 | case DEMUX_SET_PAUSE_STATE: |
846 | 0 | case DEMUX_CAN_CONTROL_PACE: |
847 | 0 | case DEMUX_GET_PTS_DELAY: |
848 | 0 | return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args ); |
849 | | |
850 | 0 | default: |
851 | 0 | break; |
852 | |
|
853 | 0 | } |
854 | 0 | return VLC_EGENERIC; |
855 | 0 | } |
856 | | |
857 | | /***************************************************************************** |
858 | | * Divers: |
859 | | *****************************************************************************/ |
860 | | |
861 | | /* PSResynch: resynch on a system startcode |
862 | | * It doesn't skip more than 512 bytes |
863 | | * -1 -> error, 0 -> not synch, 1 -> ok |
864 | | */ |
865 | | static int ps_pkt_resynch( stream_t *s, int format, bool b_pack ) |
866 | 0 | { |
867 | 0 | const uint8_t *p_peek; |
868 | 0 | ssize_t i_peek; |
869 | 0 | unsigned int i_skip; |
870 | |
|
871 | 0 | if( vlc_stream_Peek( s, &p_peek, 4 ) < 4 ) |
872 | 0 | { |
873 | 0 | return -1; |
874 | 0 | } |
875 | 0 | if( p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] == 1 && |
876 | 0 | p_peek[3] >= PS_STREAM_ID_END_STREAM ) |
877 | 0 | { |
878 | 0 | return 1; |
879 | 0 | } |
880 | | |
881 | 0 | if( ( i_peek = vlc_stream_Peek( s, &p_peek, 512 ) ) < 4 ) |
882 | 0 | { |
883 | 0 | return -1; |
884 | 0 | } |
885 | 0 | i_skip = 0; |
886 | |
|
887 | 0 | for( ;; ) |
888 | 0 | { |
889 | 0 | if( i_peek < 4 ) |
890 | 0 | { |
891 | 0 | break; |
892 | 0 | } |
893 | | /* Handle mid stream 24 bytes padding+CRC creating emulated sync codes with incorrect |
894 | | PES sizes and frelling up to UINT16_MAX bytes followed by 24 bytes CDXA Header */ |
895 | 0 | if( format == CDXA_PS && i_skip == 0 && i_peek >= 48 ) |
896 | 0 | { |
897 | 0 | const uint8_t cdxasynccode[12] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, |
898 | 0 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; |
899 | 0 | if( !memcmp( &p_peek[24], cdxasynccode, 12 ) ) |
900 | 0 | { |
901 | 0 | i_peek -= 48; |
902 | 0 | p_peek += 48; |
903 | 0 | i_skip += 48; |
904 | 0 | continue; |
905 | 0 | } |
906 | 0 | } |
907 | | |
908 | 0 | if( p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] == 1 && |
909 | 0 | p_peek[3] >= PS_STREAM_ID_END_STREAM && |
910 | 0 | ( !b_pack || p_peek[3] == PS_STREAM_ID_PACK_HEADER ) ) |
911 | 0 | { |
912 | 0 | return vlc_stream_Read( s, NULL, i_skip ) != i_skip ? -1 : 1; |
913 | 0 | } |
914 | | |
915 | 0 | p_peek++; |
916 | 0 | i_peek--; |
917 | 0 | i_skip++; |
918 | 0 | } |
919 | 0 | return vlc_stream_Read( s, NULL, i_skip ) != i_skip ? -1 : 0; |
920 | 0 | } |
921 | | |
922 | | static block_t *ps_pkt_read( stream_t *s ) |
923 | 0 | { |
924 | 0 | const uint8_t *p_peek; |
925 | 0 | int i_peek = vlc_stream_Peek( s, &p_peek, 14 ); |
926 | 0 | if( i_peek < 4 ) |
927 | 0 | return NULL; |
928 | | |
929 | 0 | int i_size = ps_pkt_size( p_peek, i_peek ); |
930 | 0 | if( i_size <= 6 && p_peek[3] > PS_STREAM_ID_PACK_HEADER ) |
931 | 0 | { |
932 | | /* Special case, search the next start code */ |
933 | 0 | i_size = 6; |
934 | 0 | for( ;; ) |
935 | 0 | { |
936 | 0 | i_peek = vlc_stream_Peek( s, &p_peek, i_size + 1024 ); |
937 | 0 | if( i_peek <= i_size + 4 ) |
938 | 0 | { |
939 | 0 | return NULL; |
940 | 0 | } |
941 | 0 | while( i_size <= i_peek - 4 ) |
942 | 0 | { |
943 | 0 | if( p_peek[i_size] == 0x00 && p_peek[i_size+1] == 0x00 && |
944 | 0 | p_peek[i_size+2] == 0x01 && p_peek[i_size+3] >= PS_STREAM_ID_END_STREAM ) |
945 | 0 | { |
946 | 0 | return vlc_stream_Block( s, i_size ); |
947 | 0 | } |
948 | 0 | i_size++; |
949 | 0 | } |
950 | 0 | } |
951 | 0 | } |
952 | 0 | else |
953 | 0 | { |
954 | | /* Normal case */ |
955 | 0 | return vlc_stream_Block( s, i_size ); |
956 | 0 | } |
957 | | |
958 | 0 | return NULL; |
959 | 0 | } |