demux: asf: check data object range when reading
[vlc.git] / modules / demux / asf / asfpacket.c
blob1a94dcf0b10d3d612683e258e45b0a0ef9dfb9cf
1 /*****************************************************************************
2 * asfpacket.c :
3 *****************************************************************************
4 * Copyright © 2001-2004, 2011, 2014 VLC authors and VideoLAN
6 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
26 #include "asfpacket.h"
27 #include <limits.h>
29 #ifndef NDEBUG
30 # define ASF_DEBUG 1
31 #endif
33 typedef struct asf_packet_t
35 uint32_t property;
36 uint32_t length;
37 uint32_t padding_length;
38 vlc_tick_t send_time;
39 bool multiple;
40 int length_type;
42 /* buffer handling for this ASF packet */
43 uint32_t i_skip;
44 const uint8_t *p_peek;
45 uint32_t left;
46 } asf_packet_t;
48 static inline int GetValue2b(uint32_t *var, const uint8_t *p, unsigned int *skip, int left, int bits)
50 switch(bits&0x03)
52 case 1:
53 if (left < 1)
54 return -1;
55 *var = p[*skip]; *skip += 1;
56 return 0;
57 case 2:
58 if (left < 2)
59 return -1;
60 *var = GetWLE(&p[*skip]); *skip += 2;
61 return 0;
62 case 3:
63 if (left < 4)
64 return -1;
65 *var = GetDWLE(&p[*skip]); *skip += 4;
66 return 0;
67 case 0:
68 default:
69 return 0;
73 static uint32_t SkipBytes( stream_t *s, uint32_t i_bytes )
75 ssize_t i_read = vlc_stream_Read( s, NULL, i_bytes );
76 return i_read > 0 ? (uint32_t) i_read : 0;
79 static int DemuxSubPayload( asf_packet_sys_t *p_packetsys,
80 uint8_t i_stream_number, block_t **pp_frame,
81 uint32_t i_sub_payload_data_length, vlc_tick_t i_pts, vlc_tick_t i_dts,
82 uint32_t i_media_object_offset, bool b_keyframe, bool b_ignore_pts )
84 /* FIXME I don't use i_media_object_number, sould I ? */
85 if( *pp_frame && i_media_object_offset == 0 )
87 p_packetsys->pf_send( p_packetsys, i_stream_number, pp_frame );
90 block_t *p_frag = vlc_stream_Block( p_packetsys->p_demux->s, i_sub_payload_data_length );
91 if( p_frag == NULL ) {
92 msg_Warn( p_packetsys->p_demux, "cannot read data" );
93 return -1;
96 p_frag->i_pts = (b_ignore_pts) ? VLC_TICK_INVALID : VLC_TICK_0 + i_pts;
97 p_frag->i_dts = VLC_TICK_0 + i_dts;
98 if ( b_keyframe )
99 p_frag->i_flags |= BLOCK_FLAG_TYPE_I;
101 block_ChainAppend( pp_frame, p_frag );
103 return 0;
106 static void ParsePayloadExtensions( asf_packet_sys_t *p_packetsys,
107 const asf_track_info_t *p_tkinfo,
108 const uint8_t *p_data, size_t i_data,
109 bool *b_keyframe )
111 demux_t *p_demux = p_packetsys->p_demux;
113 if ( !p_tkinfo || !p_tkinfo->p_esp || !p_tkinfo->p_esp->p_ext )
114 return;
116 uint16_t i_payload_extensions_size;
117 asf_payload_extension_system_t *p_ext = NULL;
119 /* Extensions always come in the declared order */
120 for ( int i=0; i< p_tkinfo->p_esp->i_payload_extension_system_count; i++ )
122 p_ext = &p_tkinfo->p_esp->p_ext[i];
123 if ( p_ext->i_data_size == 0xFFFF ) /* Variable length extension data */
125 if ( i_data < 2 ) return;
126 i_payload_extensions_size = GetWLE( p_data );
127 p_data += 2;
128 i_data -= 2;
129 i_payload_extensions_size = 0;
131 else
133 i_payload_extensions_size = p_ext->i_data_size;
136 if ( i_data < i_payload_extensions_size ) return;
138 if ( guidcmp( &p_ext->i_extension_id, &mfasf_sampleextension_outputcleanpoint_guid ) )
140 if ( i_payload_extensions_size != sizeof(uint8_t) ) goto sizeerror;
141 *b_keyframe |= *p_data;
143 else if ( guidcmp( &p_ext->i_extension_id, &asf_dvr_sampleextension_videoframe_guid ) )
145 if ( i_payload_extensions_size != sizeof(uint32_t) ) goto sizeerror;
147 uint32_t i_val = GetDWLE( p_data );
148 /* Valid keyframe must be a split frame start fragment */
149 *b_keyframe = i_val & ASF_EXTENSION_VIDEOFRAME_NEWFRAME;
150 if ( *b_keyframe )
152 /* And flagged as IFRAME */
153 *b_keyframe |= ( ( i_val & ASF_EXTENSION_VIDEOFRAME_TYPE_MASK )
154 == ASF_EXTENSION_VIDEOFRAME_IFRAME );
157 else if ( guidcmp( &p_ext->i_extension_id, &mfasf_sampleextension_pixelaspectratio_guid ) &&
158 p_packetsys->pf_setaspectratio )
160 if ( i_payload_extensions_size != sizeof(uint16_t) ) goto sizeerror;
162 p_packetsys->pf_setaspectratio( p_packetsys, p_tkinfo->p_sp->i_stream_number,
163 p_data[0], p_data[1] );
165 else if ( guidcmp( &p_ext->i_extension_id, &asf_dvr_sampleextension_timing_rep_data_guid ) )
167 if ( i_payload_extensions_size != 48 ) goto sizeerror;
168 /* const int64_t i_pts = GetQWLE(&p_data[8]); */
170 #if 0
171 else
173 msg_Dbg( p_demux, "Unknown extension " GUID_FMT, GUID_PRINT( p_ext->i_extension_id ) );
175 #endif
176 i_data -= i_payload_extensions_size;
177 p_data += i_payload_extensions_size;
180 return;
182 sizeerror:
183 msg_Warn( p_demux, "Unknown extension " GUID_FMT " data size of %u",
184 GUID_PRINT( p_ext->i_extension_id ), i_payload_extensions_size );
187 static int DemuxPayload(asf_packet_sys_t *p_packetsys, asf_packet_t *pkt, int i_payload)
189 #ifndef ASF_DEBUG
190 VLC_UNUSED( i_payload );
191 #endif
192 demux_t *p_demux = p_packetsys->p_demux;
194 if( ! pkt->left || pkt->i_skip >= pkt->left )
195 return -1;
197 bool b_packet_keyframe = pkt->p_peek[pkt->i_skip] >> 7;
198 uint8_t i_stream_number = pkt->p_peek[pkt->i_skip++] & 0x7f;
200 uint32_t i_media_object_number = 0;
201 if (GetValue2b(&i_media_object_number, pkt->p_peek, &pkt->i_skip, pkt->left - pkt->i_skip, pkt->property >> 4) < 0)
202 return -1;
203 uint32_t i_media_object_offset = 0;
204 if (GetValue2b(&i_media_object_offset, pkt->p_peek, &pkt->i_skip, pkt->left - pkt->i_skip, pkt->property >> 2) < 0)
205 return -1;
206 uint32_t i_replicated_data_length = 0;
207 if (GetValue2b(&i_replicated_data_length, pkt->p_peek, &pkt->i_skip, pkt->left - pkt->i_skip, pkt->property) < 0)
208 return -1;
210 vlc_tick_t i_pkt_time;
211 vlc_tick_t i_pkt_time_delta = 0;
212 uint32_t i_payload_data_length = 0;
213 uint32_t i_temp_payload_length = 0;
214 *p_packetsys->pi_preroll = __MIN( *p_packetsys->pi_preroll, INT64_MAX );
216 /* First packet, in case we do not have index to guess preroll start time */
217 if ( *p_packetsys->pi_preroll_start == ASFPACKET_PREROLL_FROM_CURRENT )
218 *p_packetsys->pi_preroll_start = pkt->send_time;
220 asf_track_info_t *p_tkinfo = p_packetsys->pf_gettrackinfo( p_packetsys, i_stream_number );
221 if ( !p_tkinfo )
222 goto skip;
224 bool b_ignore_pts = (p_tkinfo->i_cat == VIDEO_ES); /* ignore PTS delta with video when not set by mux */
226 if( pkt->left - pkt->i_skip < i_replicated_data_length )
227 return -1;
229 /* Non compressed */
230 if( i_replicated_data_length > 7 ) // should be at least 8 bytes
232 /* Followed by 2 optional DWORDS, offset in media and *media* presentation time */
233 i_pkt_time = VLC_TICK_FROM_MS(GetDWLE( pkt->p_peek + pkt->i_skip + 4 ));
235 /* Parsing extensions, See 7.3.1 */
236 ParsePayloadExtensions( p_packetsys, p_tkinfo,
237 &pkt->p_peek[pkt->i_skip + 8],
238 i_replicated_data_length - 8,
239 &b_packet_keyframe );
240 i_pkt_time -= *p_packetsys->pi_preroll;
241 pkt->i_skip += i_replicated_data_length;
243 else if ( i_replicated_data_length == 0 )
245 /* optional DWORDS missing */
246 i_pkt_time = pkt->send_time;
248 /* Compressed payload */
249 else if( i_replicated_data_length == 1 )
251 /* i_media_object_offset is *media* presentation time */
252 /* Next byte is *media* Presentation Time Delta */
253 i_pkt_time_delta = VLC_TICK_FROM_MS(pkt->p_peek[pkt->i_skip]);
254 b_ignore_pts = false;
255 i_pkt_time = VLC_TICK_FROM_MS(i_media_object_offset);
256 i_pkt_time -= *p_packetsys->pi_preroll;
257 pkt->i_skip++;
258 i_media_object_offset = 0;
260 else
262 /* >1 && <8 Invalid replicated length ! */
263 msg_Warn( p_demux, "Invalid replicated data length detected." );
264 if( pkt->length - pkt->i_skip < pkt->padding_length )
265 return -1;
267 i_payload_data_length = pkt->length - pkt->padding_length - pkt->i_skip;
268 goto skip;
271 if( ! pkt->left || pkt->i_skip >= pkt->left )
272 return -1;
274 bool b_preroll_done = ( pkt->send_time > (*p_packetsys->pi_preroll_start + *p_packetsys->pi_preroll) );
276 if (i_pkt_time < 0) i_pkt_time = 0; // FIXME?
278 if( pkt->multiple ) {
279 if (GetValue2b(&i_temp_payload_length, pkt->p_peek, &pkt->i_skip, pkt->left - pkt->i_skip, pkt->length_type) < 0)
280 return -1;
282 else
284 if( pkt->length - pkt->i_skip < pkt->padding_length )
285 return -1;
286 i_temp_payload_length = pkt->length - pkt->padding_length - pkt->i_skip;
289 i_payload_data_length = i_temp_payload_length;
291 #ifdef ASF_DEBUG
292 msg_Dbg( p_demux,
293 "payload(%d) stream_number:%"PRIu8" media_object_number:%d media_object_offset:%"PRIu32" replicated_data_length:%"PRIu32" payload_data_length %"PRIu32,
294 i_payload + 1, i_stream_number, i_media_object_number,
295 i_media_object_offset, i_replicated_data_length, i_payload_data_length );
296 msg_Dbg( p_demux,
297 " pkttime=%"PRId64" st=%"PRId64,
298 i_pkt_time, MS_FROM_VLC_TICK(pkt->send_time) );
299 #endif
301 if( ! i_payload_data_length || i_payload_data_length > pkt->left )
303 msg_Dbg( p_demux, " payload length problem %d %"PRIu32" %"PRIu32, pkt->multiple, i_payload_data_length, pkt->left );
304 return -1;
307 if ( p_packetsys->pf_doskip &&
308 p_packetsys->pf_doskip( p_packetsys, i_stream_number, b_packet_keyframe ) )
309 goto skip;
311 if ( b_preroll_done )
313 vlc_tick_t i_track_time = i_pkt_time;
315 if ( p_packetsys->pf_updatetime )
316 p_packetsys->pf_updatetime( p_packetsys, i_stream_number, i_track_time );
319 if( p_packetsys->pf_updatesendtime )
320 p_packetsys->pf_updatesendtime( p_packetsys, pkt->send_time );
322 uint32_t i_subpayload_count = 0;
323 while (i_payload_data_length && pkt->i_skip < pkt->left )
325 uint32_t i_sub_payload_data_length = i_payload_data_length;
326 if( i_replicated_data_length == 1 )
328 i_sub_payload_data_length = pkt->p_peek[pkt->i_skip++];
329 i_payload_data_length--;
330 if( i_sub_payload_data_length > i_payload_data_length )
331 goto skip;
334 SkipBytes( p_demux->s, pkt->i_skip );
336 vlc_tick_t i_payload_pts;
337 i_payload_pts = i_pkt_time + i_pkt_time_delta * i_subpayload_count;
338 if ( p_tkinfo->p_sp )
339 i_payload_pts -= VLC_TICK_FROM_MSFTIME(p_tkinfo->p_sp->i_time_offset);
341 vlc_tick_t i_payload_dts = i_pkt_time;
343 if ( p_tkinfo->p_sp )
344 i_payload_dts -= VLC_TICK_FROM_MSFTIME(p_tkinfo->p_sp->i_time_offset);
346 if ( i_sub_payload_data_length &&
347 DemuxSubPayload( p_packetsys, i_stream_number, &p_tkinfo->p_frame,
348 i_sub_payload_data_length, i_payload_pts, i_payload_dts,
349 i_media_object_offset, b_packet_keyframe, b_ignore_pts ) < 0)
350 return -1;
352 if ( pkt->left > pkt->i_skip + i_sub_payload_data_length )
353 pkt->left -= pkt->i_skip + i_sub_payload_data_length;
354 else
355 pkt->left = 0;
356 pkt->i_skip = 0;
357 if( pkt->left > 0 )
359 ssize_t i_return = vlc_stream_Peek( p_demux->s, &pkt->p_peek, pkt->left );
360 if ( i_return <= 0 || (size_t) i_return < pkt->left )
362 msg_Warn( p_demux, "unexpected end of file" );
363 return -1;
367 if ( i_sub_payload_data_length <= i_payload_data_length )
368 i_payload_data_length -= i_sub_payload_data_length;
369 else
370 i_payload_data_length = 0;
372 i_subpayload_count++;
375 return 0;
377 skip:
378 pkt->i_skip += i_payload_data_length;
379 return 0;
382 int DemuxASFPacket( asf_packet_sys_t *p_packetsys,
383 uint32_t i_data_packet_min, uint32_t i_data_packet_max,
384 uint64_t i_data_begin, uint64_t i_data_end )
386 demux_t *p_demux = p_packetsys->p_demux;
388 const uint64_t i_read_pos = vlc_stream_Tell( p_demux->s );
389 if( i_read_pos < i_data_begin ||
390 i_data_packet_min > i_data_end ||
391 i_read_pos > i_data_end - i_data_packet_min )
392 return 0;
394 const uint8_t *p_peek;
395 ssize_t i_return = vlc_stream_Peek( p_demux->s, &p_peek,i_data_packet_min );
396 if( i_return <= 0 || (size_t) i_return < i_data_packet_min )
398 msg_Warn( p_demux, "unexpected end of file" );
399 return 0;
401 unsigned int i_skip = 0;
403 /* *** parse error correction if present *** */
404 if( p_peek[0]&0x80 )
406 unsigned int i_error_correction_data_length = p_peek[0] & 0x0f;
407 unsigned int i_opaque_data_present = ( p_peek[0] >> 4 )& 0x01;
408 unsigned int i_error_correction_length_type = ( p_peek[0] >> 5 ) & 0x03;
409 i_skip += 1; // skip error correction flags
411 if( i_error_correction_length_type != 0x00 ||
412 i_opaque_data_present != 0 ||
413 i_error_correction_data_length != 0x02 )
415 goto loop_error_recovery;
418 i_skip += i_error_correction_data_length;
420 else
421 msg_Warn( p_demux, "no error correction" );
423 /* sanity check */
424 if( i_skip + 2 >= i_data_packet_min )
425 goto loop_error_recovery;
427 asf_packet_t pkt;
428 int i_packet_flags = p_peek[i_skip]; i_skip++;
429 pkt.property = p_peek[i_skip]; i_skip++;
430 pkt.multiple = !!(i_packet_flags&0x01);
432 pkt.length = i_data_packet_min;
433 pkt.padding_length = 0;
435 if (GetValue2b(&pkt.length, p_peek, &i_skip, i_data_packet_min - i_skip, i_packet_flags >> 5) < 0)
436 goto loop_error_recovery;
437 uint32_t i_packet_sequence;
438 if (GetValue2b(&i_packet_sequence, p_peek, &i_skip, i_data_packet_min - i_skip, i_packet_flags >> 1) < 0)
439 goto loop_error_recovery;
440 if (GetValue2b(&pkt.padding_length, p_peek, &i_skip, i_data_packet_min - i_skip, i_packet_flags >> 3) < 0)
441 goto loop_error_recovery;
443 if( pkt.padding_length > pkt.length )
445 msg_Warn( p_demux, "Too large padding: %"PRIu32, pkt.padding_length );
446 goto loop_error_recovery;
449 if( pkt.length < i_data_packet_min )
451 /* if packet length too short, there is extra padding */
452 pkt.padding_length += i_data_packet_min - pkt.length;
453 pkt.length = i_data_packet_min;
456 if( i_skip + 4 > i_data_packet_min )
457 goto loop_error_recovery;
459 pkt.send_time = VLC_TICK_FROM_MS(GetDWLE( p_peek + i_skip )); i_skip += 4;
460 /* uint16_t i_packet_duration = GetWLE( p_peek + i_skip ); */ i_skip += 2;
462 if( pkt.length > i_data_end ||
463 i_read_pos > i_data_end - pkt.length )
465 msg_Warn( p_demux, "pkt size %"PRIu32" at %"PRIu64" does not fit data chunk",
466 pkt.length, i_read_pos );
467 return 0;
470 i_return = vlc_stream_Peek( p_demux->s, &p_peek, pkt.length );
471 if( i_return <= 0 || pkt.length == 0 || (size_t)i_return < pkt.length )
473 msg_Warn( p_demux, "unexpected end of file" );
474 return 0;
477 int i_payload_count = 1;
478 pkt.length_type = 0x02; //unused
479 if( pkt.multiple )
481 if( i_skip + 1 >= i_data_packet_min )
482 goto loop_error_recovery;
483 i_payload_count = p_peek[i_skip] & 0x3f;
484 pkt.length_type = ( p_peek[i_skip] >> 6 )&0x03;
485 i_skip++;
488 #ifdef ASF_DEBUG
489 msg_Dbg(p_demux, "%d payloads", i_payload_count);
490 #endif
492 pkt.i_skip = i_skip;
493 pkt.p_peek = p_peek;
494 pkt.left = pkt.length;
496 for( int i_payload = 0; i_payload < i_payload_count ; i_payload++ )
497 if (DemuxPayload(p_packetsys, &pkt, i_payload) < 0)
499 msg_Warn( p_demux, "payload err %d / %d", i_payload + 1, i_payload_count );
500 return 0;
503 if( pkt.left > 0 )
505 #ifdef ASF_DEBUG
506 if( pkt.left > pkt.padding_length )
507 msg_Warn( p_demux, "Didn't read %"PRIu32" bytes in the packet",
508 pkt.left - pkt.padding_length );
509 else if( pkt.left < pkt.padding_length )
510 msg_Warn( p_demux, "Read %"PRIu32" too much bytes in the packet",
511 pkt.padding_length - pkt.left );
512 #endif
513 i_return = vlc_stream_Read( p_demux->s, NULL, pkt.left );
514 if( i_return < 0 || (size_t) i_return < pkt.left )
516 msg_Err( p_demux, "cannot skip data, EOF ?" );
517 return 0;
521 return 1;
523 loop_error_recovery:
524 msg_Warn( p_demux, "unsupported packet header" );
525 if( i_data_packet_min != i_data_packet_max )
527 msg_Err( p_demux, "unsupported packet header, fatal error" );
528 return -1;
530 i_return = vlc_stream_Read( p_demux->s, NULL, i_data_packet_min );
531 if( i_return <= 0 || (size_t) i_return != i_data_packet_min )
533 msg_Warn( p_demux, "cannot skip data, EOF ?" );
534 return 0;
537 return 1;