| | 237 | static int64_t ControlGetLength( demux_t *p_demux ) |
|---|
| | 238 | { |
|---|
| | 239 | demux_sys_t *p_sys = p_demux->p_sys; |
|---|
| | 240 | const int64_t i_size = stream_Size(p_demux->s) - p_sys->i_data_pos; |
|---|
| | 241 | int64_t i_length = p_sys->i_length; |
|---|
| | 242 | int i; |
|---|
| | 243 | |
|---|
| | 244 | /* Try to fix length using seekpoint and current size for truncated file */ |
|---|
| | 245 | for( i = p_sys->i_seekpoint-1; i >= 0; i-- ) |
|---|
| | 246 | { |
|---|
| | 247 | seekpoint_t *s = p_sys->seekpoint[i]; |
|---|
| | 248 | if( s->i_byte_offset <= i_size ) |
|---|
| | 249 | { |
|---|
| | 250 | if( i+1 < p_sys->i_seekpoint ) |
|---|
| | 251 | { |
|---|
| | 252 | /* Broken file */ |
|---|
| | 253 | seekpoint_t *n = p_sys->seekpoint[i+1]; |
|---|
| | 254 | assert( n->i_byte_offset != s->i_byte_offset); /* Should be ensured by ParseSeekTable */ |
|---|
| | 255 | i_length = s->i_time_offset + (n->i_time_offset-s->i_time_offset) * (i_size-s->i_byte_offset) / (n->i_byte_offset-s->i_byte_offset); |
|---|
| | 256 | } |
|---|
| | 257 | break; |
|---|
| | 258 | } |
|---|
| | 259 | } |
|---|
| | 260 | return i_length; |
|---|
| | 261 | } |
|---|
| | 262 | static int64_t ControlGetTime( demux_t *p_demux ) |
|---|
| | 263 | { |
|---|
| | 264 | demux_sys_t *p_sys = p_demux->p_sys; |
|---|
| | 265 | return __MAX(p_sys->i_pts, p_sys->i_pts_start) + p_sys->i_time_offset; |
|---|
| | 266 | } |
|---|
| | 267 | static int ControlSetTime( demux_t *p_demux, int64_t i_time ) |
|---|
| | 268 | { |
|---|
| | 269 | demux_sys_t *p_sys = p_demux->p_sys; |
|---|
| | 270 | int64_t i_next_time; |
|---|
| | 271 | int64_t i_next_offset; |
|---|
| | 272 | int64_t i_delta_time; |
|---|
| | 273 | int64_t i_delta_offset; |
|---|
| | 274 | vlc_bool_t b_seekable; |
|---|
| | 275 | int i; |
|---|
| | 276 | |
|---|
| | 277 | /* */ |
|---|
| | 278 | stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable ); |
|---|
| | 279 | if( !b_seekable ) |
|---|
| | 280 | return VLC_EGENERIC; |
|---|
| | 281 | |
|---|
| | 282 | /* */ |
|---|
| | 283 | assert( p_sys->i_seekpoint > 0 ); /* ReadMeta ensure at least (0,0) */ |
|---|
| | 284 | for( i = p_sys->i_seekpoint-1; i >= 0; i-- ) |
|---|
| | 285 | { |
|---|
| | 286 | if( p_sys->seekpoint[i]->i_time_offset <= i_time ) |
|---|
| | 287 | break; |
|---|
| | 288 | } |
|---|
| | 289 | if( i+1 < p_sys->i_seekpoint ) |
|---|
| | 290 | { |
|---|
| | 291 | i_next_time = p_sys->seekpoint[i+1]->i_time_offset; |
|---|
| | 292 | i_next_offset = p_sys->seekpoint[i+1]->i_byte_offset; |
|---|
| | 293 | } |
|---|
| | 294 | else |
|---|
| | 295 | { |
|---|
| | 296 | i_next_time = p_sys->i_length; |
|---|
| | 297 | i_next_offset = stream_Size(p_demux->s)-p_sys->i_data_pos; |
|---|
| | 298 | } |
|---|
| | 299 | i_delta_time = i_time - p_sys->seekpoint[i]->i_time_offset; |
|---|
| | 300 | i_delta_offset = (i_next_offset - p_sys->seekpoint[i]->i_byte_offset) * i_delta_time / |
|---|
| | 301 | (p_sys->seekpoint[i+1]->i_time_offset-p_sys->seekpoint[i]->i_time_offset); |
|---|
| | 302 | |
|---|
| | 303 | /* XXX We do exact seek if it's not too far away(45s) */ |
|---|
| | 304 | if( i_delta_time < 45*I64C(1000000) ) |
|---|
| | 305 | { |
|---|
| | 306 | if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos ) ) |
|---|
| | 307 | return VLC_EGENERIC; |
|---|
| | 308 | p_sys->i_time_offset = p_sys->seekpoint[i]->i_time_offset - p_sys->i_pts; |
|---|
| | 309 | p_sys->i_pts_start = p_sys->i_pts+i_delta_time; |
|---|
| | 310 | es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, p_sys->p_es, p_sys->i_pts_start ); |
|---|
| | 311 | } |
|---|
| | 312 | else |
|---|
| | 313 | { |
|---|
| | 314 | if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos + i_delta_offset ) ) |
|---|
| | 315 | return VLC_EGENERIC; |
|---|
| | 316 | p_sys->i_pts_start = p_sys->i_pts; |
|---|
| | 317 | p_sys->i_time_offset = (p_sys->seekpoint[i]->i_time_offset+i_delta_time) - p_sys->i_pts; |
|---|
| | 318 | } |
|---|
| | 319 | return VLC_SUCCESS; |
|---|
| | 320 | } |
|---|
| | 321 | |
|---|
| 230 | | else return demux2_vaControlHelper( p_demux->s, 0, -1, |
|---|
| 231 | | 8*0, 1, i_query, args ); |
|---|
| 232 | | } |
|---|
| 233 | | |
|---|
| | 333 | else if( i_query == DEMUX_GET_LENGTH ) |
|---|
| | 334 | { |
|---|
| | 335 | int64_t *pi64 = (int64_t*)va_arg( args, int64_t * ); |
|---|
| | 336 | *pi64 = ControlGetLength( p_demux ); |
|---|
| | 337 | return VLC_SUCCESS; |
|---|
| | 338 | } |
|---|
| | 339 | else if( i_query == DEMUX_SET_TIME ) |
|---|
| | 340 | { |
|---|
| | 341 | int64_t i_time = (int64_t)va_arg( args, int64_t ); |
|---|
| | 342 | return ControlSetTime( p_demux, i_time ); |
|---|
| | 343 | } |
|---|
| | 344 | else if( i_query == DEMUX_SET_POSITION ) |
|---|
| | 345 | { |
|---|
| | 346 | const double f = (double)va_arg( args, double ); |
|---|
| | 347 | int64_t i_time = f * ControlGetLength( p_demux ); |
|---|
| | 348 | return ControlSetTime( p_demux, i_time ); |
|---|
| | 349 | } |
|---|
| | 350 | else if( i_query == DEMUX_GET_TIME ) |
|---|
| | 351 | { |
|---|
| | 352 | int64_t *pi64 = (int64_t*)va_arg( args, int64_t * ); |
|---|
| | 353 | *pi64 = ControlGetTime( p_demux ); |
|---|
| | 354 | return VLC_SUCCESS; |
|---|
| | 355 | } |
|---|
| | 356 | else if( i_query == DEMUX_GET_POSITION ) |
|---|
| | 357 | { |
|---|
| | 358 | double *pf = (double*)va_arg( args, double * ); |
|---|
| | 359 | const int64_t i_length = ControlGetLength(p_demux); |
|---|
| | 360 | if( i_length > 0 ) |
|---|
| | 361 | *pf = (double)ControlGetTime(p_demux) / (double)i_length; |
|---|
| | 362 | else |
|---|
| | 363 | *pf= 0.0; |
|---|
| | 364 | return VLC_SUCCESS; |
|---|
| | 365 | } |
|---|
| | 366 | |
|---|
| | 367 | return demux2_vaControlHelper( p_demux->s, p_sys->i_data_pos, -1, |
|---|
| | 368 | 8*0, 1, i_query, args ); |
|---|
| | 369 | } |
|---|
| | 370 | |
|---|
| | 371 | enum |
|---|
| | 372 | { |
|---|
| | 373 | META_STREAMINFO = 0, |
|---|
| | 374 | META_SEEKTABLE = 3, |
|---|
| | 375 | META_COMMENT = 4, |
|---|
| | 376 | }; |
|---|
| | 377 | |
|---|
| | 378 | static inline int Get24bBE( uint8_t *p ) |
|---|
| | 379 | { |
|---|
| | 380 | return (p[0] << 16)|(p[1] << 8)|(p[2]); |
|---|
| | 381 | } |
|---|
| | 382 | |
|---|
| | 383 | static void ParseStreamInfo( demux_t *p_demux, int *pi_rate, int64_t *pi_count, uint8_t *p_data, int i_data ); |
|---|
| | 384 | static void ParseSeekTable( demux_t *p_demux, uint8_t *p_data, int i_data, int i_sample_rate ); |
|---|
| | 385 | static void ParseComment( demux_t *, uint8_t *p_data, int i_data ); |
|---|
| | 386 | |
|---|
| | 387 | static int ReadMeta( demux_t *p_demux, uint8_t **pp_streaminfo, int *pi_streaminfo ) |
|---|
| | 388 | { |
|---|
| | 389 | demux_sys_t *p_sys = p_demux->p_sys; |
|---|
| | 390 | int i_peek; |
|---|
| | 391 | uint8_t *p_peek; |
|---|
| | 392 | vlc_bool_t b_last; |
|---|
| | 393 | int i_sample_rate; |
|---|
| | 394 | int64_t i_sample_count; |
|---|
| | 395 | seekpoint_t *s; |
|---|
| | 396 | |
|---|
| | 397 | /* Read STREAMINFO */ |
|---|
| | 398 | i_peek = stream_Peek( p_demux->s, &p_peek, 8 ); |
|---|
| | 399 | if( (p_peek[4] & 0x7F) != META_STREAMINFO ) |
|---|
| | 400 | { |
|---|
| | 401 | msg_Err( p_demux, "this isn't a STREAMINFO metadata block" ); |
|---|
| | 402 | return VLC_EGENERIC; |
|---|
| | 403 | } |
|---|
| | 404 | if( Get24bBE(&p_peek[5]) != (STREAMINFO_SIZE - 4) ) |
|---|
| | 405 | { |
|---|
| | 406 | msg_Err( p_demux, "invalid size for a STREAMINFO metadata block" ); |
|---|
| | 407 | return VLC_EGENERIC; |
|---|
| | 408 | } |
|---|
| | 409 | |
|---|
| | 410 | *pi_streaminfo = 4 + STREAMINFO_SIZE; |
|---|
| | 411 | *pp_streaminfo = malloc( 4 + STREAMINFO_SIZE ); |
|---|
| | 412 | if( *pp_streaminfo == NULL ) |
|---|
| | 413 | return VLC_EGENERIC; |
|---|
| | 414 | |
|---|
| | 415 | if( stream_Read( p_demux->s, *pp_streaminfo, 4+STREAMINFO_SIZE ) != 4+STREAMINFO_SIZE ) |
|---|
| | 416 | { |
|---|
| | 417 | msg_Err( p_demux, "failed to read STREAMINFO metadata block" ); |
|---|
| | 418 | free( *pp_streaminfo ); |
|---|
| | 419 | return VLC_EGENERIC; |
|---|
| | 420 | } |
|---|
| | 421 | |
|---|
| | 422 | /* */ |
|---|
| | 423 | ParseStreamInfo( p_demux, &i_sample_rate, &i_sample_count, *pp_streaminfo, *pi_streaminfo ); |
|---|
| | 424 | if( i_sample_rate > 0 ) |
|---|
| | 425 | p_sys->i_length = i_sample_count * I64C(1000000)/i_sample_rate; |
|---|
| | 426 | |
|---|
| | 427 | /* Be sure we have seekpoint 0 */ |
|---|
| | 428 | s = vlc_seekpoint_New(); |
|---|
| | 429 | s->i_time_offset = 0; |
|---|
| | 430 | s->i_byte_offset = 0; |
|---|
| | 431 | TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s ); |
|---|
| | 432 | |
|---|
| | 433 | |
|---|
| | 434 | |
|---|
| | 435 | b_last = (*pp_streaminfo)[4]&0x80; |
|---|
| | 436 | while( !b_last ) |
|---|
| | 437 | { |
|---|
| | 438 | int i_len; |
|---|
| | 439 | int i_type; |
|---|
| | 440 | |
|---|
| | 441 | i_peek = stream_Peek( p_demux->s, &p_peek, 4 ); |
|---|
| | 442 | if( i_peek < 4 ) |
|---|
| | 443 | break; |
|---|
| | 444 | b_last = p_peek[0]&0x80; |
|---|
| | 445 | i_type = p_peek[0]&0x7f; |
|---|
| | 446 | i_len = Get24bBE( &p_peek[1] ); |
|---|
| | 447 | |
|---|
| | 448 | if( i_type == META_SEEKTABLE ) |
|---|
| | 449 | { |
|---|
| | 450 | i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len ); |
|---|
| | 451 | if( i_peek == 4+i_len ) |
|---|
| | 452 | ParseSeekTable( p_demux, p_peek, i_peek, i_sample_rate ); |
|---|
| | 453 | } |
|---|
| | 454 | else if( i_type == META_COMMENT ) |
|---|
| | 455 | { |
|---|
| | 456 | i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len ); |
|---|
| | 457 | if( i_peek == 4+i_len ) |
|---|
| | 458 | ParseComment( p_demux, p_peek, i_peek ); |
|---|
| | 459 | } |
|---|
| | 460 | |
|---|
| | 461 | if( stream_Read( p_demux->s, NULL, 4+i_len ) < 4+i_len ) |
|---|
| | 462 | break; |
|---|
| | 463 | } |
|---|
| | 464 | |
|---|
| | 465 | /* */ |
|---|
| | 466 | p_sys->i_data_pos = stream_Tell( p_demux->s ); |
|---|
| | 467 | |
|---|
| | 468 | return VLC_SUCCESS; |
|---|
| | 469 | } |
|---|
| | 470 | static void ParseStreamInfo( demux_t *p_demux, int *pi_rate, int64_t *pi_count, uint8_t *p_data, int i_data ) |
|---|
| | 471 | { |
|---|
| | 472 | const int i_skip = 4+4; |
|---|
| | 473 | |
|---|
| | 474 | *pi_rate = GetDWBE(&p_data[i_skip+4+6]) >> 12; |
|---|
| | 475 | *pi_count = GetQWBE(&p_data[i_skip+4+6]) & ((I64C(1)<<36)-1); |
|---|
| | 476 | } |
|---|
| | 477 | static void ParseSeekTable( demux_t *p_demux, uint8_t *p_data, int i_data, int i_sample_rate ) |
|---|
| | 478 | { |
|---|
| | 479 | demux_sys_t *p_sys = p_demux->p_sys; |
|---|
| | 480 | seekpoint_t *s; |
|---|
| | 481 | int i; |
|---|
| | 482 | |
|---|
| | 483 | if( i_sample_rate <= 0 ) |
|---|
| | 484 | return; |
|---|
| | 485 | |
|---|
| | 486 | /* */ |
|---|
| | 487 | for( i = 0; i < (i_data-4)/18; i++ ) |
|---|
| | 488 | { |
|---|
| | 489 | const int64_t i_sample = GetQWBE( &p_data[4+18*i+0] ); |
|---|
| | 490 | int j; |
|---|
| | 491 | |
|---|
| | 492 | if( i_sample < 0 || i_sample >= INT64_MAX ) |
|---|
| | 493 | continue; |
|---|
| | 494 | |
|---|
| | 495 | s = vlc_seekpoint_New(); |
|---|
| | 496 | s->i_time_offset = i_sample * I64C(1000000)/i_sample_rate; |
|---|
| | 497 | s->i_byte_offset = GetQWBE( &p_data[4+18*i+8] ); |
|---|
| | 498 | |
|---|
| | 499 | /* Check for duplicate entry */ |
|---|
| | 500 | for( j = 0; j < p_sys->i_seekpoint; j++ ) |
|---|
| | 501 | { |
|---|
| | 502 | if( p_sys->seekpoint[j]->i_time_offset == s->i_time_offset || |
|---|
| | 503 | p_sys->seekpoint[j]->i_byte_offset == s->i_byte_offset ) |
|---|
| | 504 | { |
|---|
| | 505 | vlc_seekpoint_Delete( s ); |
|---|
| | 506 | s = NULL; |
|---|
| | 507 | break; |
|---|
| | 508 | } |
|---|
| | 509 | } |
|---|
| | 510 | if( s ) |
|---|
| | 511 | { |
|---|
| | 512 | TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s ); |
|---|
| | 513 | } |
|---|
| | 514 | } |
|---|
| | 515 | /* TODO sort it by size and remove wrong seek entry (time not increasing) */ |
|---|
| | 516 | } |
|---|
| | 517 | static inline void astrcat( char **ppsz_dst, const char *psz_src ) |
|---|
| | 518 | { |
|---|
| | 519 | char *psz_old = *ppsz_dst; |
|---|
| | 520 | |
|---|
| | 521 | if( !psz_src || !psz_src[0] ) |
|---|
| | 522 | return; |
|---|
| | 523 | |
|---|
| | 524 | if( psz_old ) |
|---|
| | 525 | { |
|---|
| | 526 | asprintf( ppsz_dst, "%s,%s", psz_old, psz_src ); |
|---|
| | 527 | free( psz_old ); |
|---|
| | 528 | } |
|---|
| | 529 | else |
|---|
| | 530 | { |
|---|
| | 531 | *ppsz_dst = strdup( psz_src ); |
|---|
| | 532 | } |
|---|
| | 533 | } |
|---|
| | 534 | |
|---|
| | 535 | static void ParseComment( demux_t *p_demux, uint8_t *p_data, int i_data ) |
|---|
| | 536 | { |
|---|
| | 537 | demux_sys_t *p_sys = p_demux->p_sys; |
|---|
| | 538 | int n; |
|---|
| | 539 | int i_comment; |
|---|
| | 540 | |
|---|
| | 541 | if( i_data < 8 ) |
|---|
| | 542 | return; |
|---|
| | 543 | |
|---|
| | 544 | #define RM(x) do { i_data -= (x); p_data += (x); } while(0) |
|---|
| | 545 | RM(4); |
|---|
| | 546 | |
|---|
| | 547 | n = GetDWLE(p_data); RM(4); |
|---|
| | 548 | if( n < 0 || n > i_data ) |
|---|
| | 549 | return; |
|---|
| | 550 | #if 0 |
|---|
| | 551 | if( n > 0 ) |
|---|
| | 552 | { |
|---|
| | 553 | /* TODO report vendor string ? */ |
|---|
| | 554 | char *psz_vendor = psz_vendor = strndup( p_data, n ); |
|---|
| | 555 | msg_Dbg( p_demux, "FLAC: COMMENT vendor length=%d vendor=%s\n", n, psz_vendor ); |
|---|
| | 556 | free( psz_vendor ); |
|---|
| | 557 | } |
|---|
| | 558 | #endif |
|---|
| | 559 | RM(n); |
|---|
| | 560 | |
|---|
| | 561 | if( i_data < 4 ) |
|---|
| | 562 | return; |
|---|
| | 563 | |
|---|
| | 564 | i_comment = GetDWLE(p_data); RM(4); |
|---|
| | 565 | if( i_comment <= 0 ) |
|---|
| | 566 | return; |
|---|
| | 567 | |
|---|
| | 568 | p_sys->p_meta = vlc_meta_New(); |
|---|
| | 569 | |
|---|
| | 570 | for( ; i_comment > 0; i_comment-- ) |
|---|
| | 571 | { |
|---|
| | 572 | char *psz; |
|---|
| | 573 | if( i_data < 4 ) |
|---|
| | 574 | break; |
|---|
| | 575 | n = GetDWLE(p_data); RM(4); |
|---|
| | 576 | if( n > i_data ) |
|---|
| | 577 | break; |
|---|
| | 578 | if( n <= 0 ) |
|---|
| | 579 | continue; |
|---|
| | 580 | |
|---|
| | 581 | psz = strndup( p_data, n ); |
|---|
| | 582 | RM(n); |
|---|
| | 583 | |
|---|
| | 584 | EnsureUTF8( psz ); |
|---|
| | 585 | |
|---|
| | 586 | #define IF_EXTRACT(txt,var) if( !strncasecmp(psz, txt, strlen(txt)) ) { astrcat( &p_sys->p_meta->var, &psz[strlen(txt)] ); } |
|---|
| | 587 | IF_EXTRACT("TITLE=", psz_title ) |
|---|
| | 588 | else IF_EXTRACT("ALBUM=", psz_album ) |
|---|
| | 589 | else IF_EXTRACT("TRACKNUMBER=", psz_tracknum ) |
|---|
| | 590 | else IF_EXTRACT("ARTIST=", psz_artist ) |
|---|
| | 591 | else IF_EXTRACT("COPYRIGHT=", psz_copyright ) |
|---|
| | 592 | else IF_EXTRACT("DESCRIPTION=", psz_description ) |
|---|
| | 593 | else IF_EXTRACT("GENRE=", psz_genre ) |
|---|
| | 594 | else IF_EXTRACT("DATE=", psz_date ) |
|---|
| | 595 | else if( strchr( psz, '=' ) ) |
|---|
| | 596 | { |
|---|
| | 597 | /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC and undocumented tags) */ |
|---|
| | 598 | char *p = strchr( psz, '=' ); |
|---|
| | 599 | *p++ = '\0'; |
|---|
| | 600 | vlc_meta_AddExtra( p_sys->p_meta, psz, p ); |
|---|
| | 601 | } |
|---|
| | 602 | #undef IF_EXTRACT |
|---|
| | 603 | free( psz ); |
|---|
| | 604 | } |
|---|
| | 605 | #undef RM |
|---|
| | 606 | } |
|---|
| | 607 | |
|---|