diff options
author | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-03-13 19:25:20 +0000 |
---|---|---|
committer | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-03-13 19:25:20 +0000 |
commit | ff1b2b7fab5bbfa9f81173cdb77a34f08231d0d9 (patch) | |
tree | 7c719e99a78ff3ef39eba3c16fb6cab0f2537d1c | |
parent | 92183d2dd0275ed91a635733cc5f3d85902a9373 (diff) | |
download | rockbox-ff1b2b7fab5bbfa9f81173cdb77a34f08231d0d9.tar.gz rockbox-ff1b2b7fab5bbfa9f81173cdb77a34f08231d0d9.zip |
Refactor reading of Xing/Info/Vbri tags to prepare for further changes.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29582 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/mp3data.c | 299 |
1 files changed, 141 insertions, 158 deletions
diff --git a/apps/mp3data.c b/apps/mp3data.c index f5ef122801..35b4608396 100644 --- a/apps/mp3data.c +++ b/apps/mp3data.c | |||
@@ -349,32 +349,135 @@ unsigned long mem_find_next_frame(int startpos, long *offset, long max_offset, | |||
349 | } | 349 | } |
350 | #endif | 350 | #endif |
351 | 351 | ||
352 | int get_mp3file_info(int fd, struct mp3info *info) | 352 | /* Extract information from a 'Xing' or 'Info' header. */ |
353 | static void get_xing_info(struct mp3info *info, unsigned char *buf) | ||
353 | { | 354 | { |
354 | unsigned char frame[1800]; | 355 | int i = 8; |
355 | unsigned char *vbrheader; | 356 | |
356 | unsigned long header; | 357 | /* Is it a VBR file? */ |
357 | long bytecount; | 358 | info->is_vbr = !memcmp(buf, "Xing", 4); |
358 | int num_offsets; | ||
359 | int i; | ||
360 | long offset; | ||
361 | int j; | ||
362 | long tmp; | ||
363 | 359 | ||
364 | header = find_next_frame(fd, &bytecount, 0x20000, 0); | 360 | if (buf[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */ |
365 | /* Quit if we haven't found a valid header within 128K */ | 361 | { |
362 | info->frame_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]); | ||
363 | if (info->frame_count <= ULONG_MAX / info->ft_num) | ||
364 | info->file_time = info->frame_count * info->ft_num / info->ft_den; | ||
365 | else | ||
366 | info->file_time = info->frame_count / info->ft_den * info->ft_num; | ||
367 | i += 4; | ||
368 | } | ||
369 | |||
370 | if (buf[7] & VBR_BYTES_FLAG) /* Is byte count there? */ | ||
371 | { | ||
372 | info->byte_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]); | ||
373 | i += 4; | ||
374 | } | ||
375 | |||
376 | if (info->file_time && info->byte_count) | ||
377 | { | ||
378 | if (info->byte_count <= (ULONG_MAX/8)) | ||
379 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
380 | else | ||
381 | info->bitrate = info->byte_count / (info->file_time >> 3); | ||
382 | } | ||
383 | |||
384 | if (buf[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */ | ||
385 | { | ||
386 | info->has_toc = true; | ||
387 | memcpy( info->toc, buf+i, 100 ); | ||
388 | i += 100; | ||
389 | } | ||
390 | if (buf[7] & VBR_QUALITY_FLAG) | ||
391 | { | ||
392 | /* We don't care about this, but need to skip it */ | ||
393 | i += 4; | ||
394 | } | ||
395 | #if CONFIG_CODEC==SWCODEC | ||
396 | i += 21; | ||
397 | info->enc_delay = ((int)buf[i ] << 4) | (buf[i+1] >> 4); | ||
398 | info->enc_padding = ((int)buf[i+1] << 8) | buf[i+2]; | ||
399 | /* TODO: This sanity checking is rather silly, seeing as how the LAME | ||
400 | header contains a CRC field that can be used to verify integrity. */ | ||
401 | if (!(info->enc_delay >= 0 && info->enc_delay <= 2880 && | ||
402 | info->enc_padding >= 0 && info->enc_padding <= 2*1152)) | ||
403 | { | ||
404 | /* Invalid data */ | ||
405 | info->enc_delay = -1; | ||
406 | info->enc_padding = -1; | ||
407 | } | ||
408 | #endif | ||
409 | } | ||
410 | |||
411 | /* Extract information from a 'VBRI' header. */ | ||
412 | static void get_vbri_info(struct mp3info *info, unsigned char *buf) | ||
413 | { | ||
414 | int i, num_offsets, offset = 0; | ||
415 | |||
416 | info->is_vbr = true; /* Yes, it is a FhG VBR file */ | ||
417 | info->has_toc = false; /* We don't parse the TOC (yet) */ | ||
418 | |||
419 | info->byte_count = bytes2int(buf[10], buf[11], buf[12], buf[13]); | ||
420 | info->frame_count = bytes2int(buf[14], buf[15], buf[16], buf[17]); | ||
421 | if (info->frame_count <= ULONG_MAX / info->ft_num) | ||
422 | info->file_time = info->frame_count * info->ft_num / info->ft_den; | ||
423 | else | ||
424 | info->file_time = info->frame_count / info->ft_den * info->ft_num; | ||
425 | |||
426 | if (info->byte_count <= (ULONG_MAX/8)) | ||
427 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
428 | else | ||
429 | info->bitrate = info->byte_count / (info->file_time >> 3); | ||
430 | |||
431 | /* We don't parse the TOC, since we don't yet know how to (FIXME) */ | ||
432 | num_offsets = bytes2int(0, 0, buf[18], buf[19]); | ||
433 | VDEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n", | ||
434 | info->bitrate, info->frame_size, info->frame_size); | ||
435 | VDEBUGF("Frame count: %lx\n", info->frame_count); | ||
436 | VDEBUGF("Byte count: %lx\n", info->byte_count); | ||
437 | VDEBUGF("Offsets: %d\n", num_offsets); | ||
438 | VDEBUGF("Frames/entry: %ld\n", | ||
439 | bytes2int(0, 0, buf[24], buf[25])); | ||
440 | |||
441 | for(i = 0; i < num_offsets; i++) | ||
442 | { | ||
443 | offset += bytes2int(0, 0, buf[26+i*2], buf[27+i*2]);; | ||
444 | VDEBUGF("%03d: %lx\n", i, offset - bytecount,); | ||
445 | } | ||
446 | } | ||
447 | |||
448 | /* Seek to next mpeg header and extract relevant information. */ | ||
449 | static int get_next_header_info(int fd, long *bytecount, struct mp3info *info) | ||
450 | { | ||
451 | unsigned long header = find_next_frame(fd, bytecount, 0x20000, 0); | ||
366 | if(header == 0) | 452 | if(header == 0) |
367 | return -1; | 453 | return -1; |
368 | 454 | ||
455 | if(!mp3headerinfo(info, header)) | ||
456 | return -2; | ||
457 | |||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | int get_mp3file_info(int fd, struct mp3info *info) | ||
462 | { | ||
463 | unsigned char frame[1800], *vbrheader; | ||
464 | long bytecount; | ||
465 | int result; | ||
466 | |||
467 | /* Initialize info and frame */ | ||
369 | memset(info, 0, sizeof(struct mp3info)); | 468 | memset(info, 0, sizeof(struct mp3info)); |
370 | memset(frame, 0, sizeof(frame)); | 469 | memset(frame, 0, sizeof(frame)); |
470 | |||
371 | #if CONFIG_CODEC==SWCODEC | 471 | #if CONFIG_CODEC==SWCODEC |
372 | /* These two are needed for proper LAME gapless MP3 playback */ | 472 | /* These two are needed for proper LAME gapless MP3 playback */ |
373 | info->enc_delay = -1; | 473 | info->enc_delay = -1; |
374 | info->enc_padding = -1; | 474 | info->enc_padding = -1; |
375 | #endif | 475 | #endif |
376 | if(!mp3headerinfo(info, header)) | 476 | |
377 | return -2; | 477 | /* Get the very first MPEG frame. */ |
478 | result = get_next_header_info(fd, &bytecount, info); | ||
479 | if(result) | ||
480 | return result; | ||
378 | 481 | ||
379 | /* OK, we have found a frame. Let's see if it has a Xing header */ | 482 | /* OK, we have found a frame. Let's see if it has a Xing header */ |
380 | if (info->frame_size-4 >= (int)sizeof(frame)) | 483 | if (info->frame_size-4 >= (int)sizeof(frame)) |
@@ -386,170 +489,50 @@ int get_mp3file_info(int fd, struct mp3info *info) | |||
386 | if(read(fd, frame, info->frame_size-4) < 0) | 489 | if(read(fd, frame, info->frame_size-4) < 0) |
387 | return -3; | 490 | return -3; |
388 | 491 | ||
389 | /* calculate position of VBR header */ | 492 | /* Calculate position of a possible VBR header */ |
390 | if ( info->version == MPEG_VERSION1 ) { | 493 | if (info->version == MPEG_VERSION1) { |
391 | if (info->channel_mode == 3) /* mono */ | 494 | if (info->channel_mode == 3) /* mono */ |
392 | vbrheader = frame + 17; | 495 | vbrheader = frame + 17; |
393 | else | 496 | else |
394 | vbrheader = frame + 32; | 497 | vbrheader = frame + 32; |
395 | } | 498 | } else { |
396 | else { | ||
397 | if (info->channel_mode == 3) /* mono */ | 499 | if (info->channel_mode == 3) /* mono */ |
398 | vbrheader = frame + 9; | 500 | vbrheader = frame + 9; |
399 | else | 501 | else |
400 | vbrheader = frame + 17; | 502 | vbrheader = frame + 17; |
401 | } | 503 | } |
402 | 504 | ||
403 | if (!memcmp(vbrheader, "Xing", 4) | 505 | if (!memcmp(vbrheader, "Xing", 4) || !memcmp(vbrheader, "Info", 4)) |
404 | || !memcmp(vbrheader, "Info", 4)) | ||
405 | { | 506 | { |
406 | int i = 8; /* Where to start parsing info */ | 507 | VDEBUGF("-- XING header --\n"); |
407 | |||
408 | /* DEBUGF("Xing/Info header\n"); */ | ||
409 | 508 | ||
410 | /* Remember where in the file the Xing header is */ | ||
411 | /* Rockbox: not used | ||
412 | info->vbr_header_pos = lseek(fd, 0, SEEK_CUR) - info->frame_size; | ||
413 | */ | ||
414 | /* We want to skip the Xing frame when playing the stream */ | 509 | /* We want to skip the Xing frame when playing the stream */ |
415 | bytecount += info->frame_size; | 510 | bytecount += info->frame_size; |
416 | 511 | ||
417 | /* Now get the next frame to find out the real info about | 512 | /* Now get the next frame to read the real info about the mp3 stream */ |
418 | the mp3 stream */ | 513 | result = get_next_header_info(fd, &bytecount, info); |
419 | header = find_next_frame(fd, &tmp, 0x20000, 0); | 514 | if(result) |
420 | if(header == 0) | 515 | return result; |
421 | return -4; | 516 | |
422 | 517 | get_xing_info(info, vbrheader); | |
423 | if(!mp3headerinfo(info, header)) | ||
424 | return -5; | ||
425 | |||
426 | /* Is it a VBR file? */ | ||
427 | info->is_vbr = !memcmp(vbrheader, "Xing", 4); | ||
428 | /* Rockbox: not used | ||
429 | info->is_xing_vbr = info->is_vbr; | ||
430 | */ | ||
431 | |||
432 | if (vbrheader[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */ | ||
433 | { | ||
434 | info->frame_count = bytes2int(vbrheader[i], vbrheader[i+1], | ||
435 | vbrheader[i+2], vbrheader[i+3]); | ||
436 | if (info->frame_count <= ULONG_MAX / info->ft_num) | ||
437 | info->file_time = info->frame_count * info->ft_num / info->ft_den; | ||
438 | else | ||
439 | info->file_time = info->frame_count / info->ft_den * info->ft_num; | ||
440 | i += 4; | ||
441 | } | ||
442 | |||
443 | if (vbrheader[7] & VBR_BYTES_FLAG) /* Is byte count there? */ | ||
444 | { | ||
445 | info->byte_count = bytes2int(vbrheader[i], vbrheader[i+1], | ||
446 | vbrheader[i+2], vbrheader[i+3]); | ||
447 | i += 4; | ||
448 | } | ||
449 | |||
450 | if (info->file_time && info->byte_count) | ||
451 | { | ||
452 | if (info->byte_count <= (ULONG_MAX/8)) | ||
453 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
454 | else | ||
455 | info->bitrate = info->byte_count / (info->file_time >> 3); | ||
456 | } | ||
457 | |||
458 | if (vbrheader[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */ | ||
459 | { | ||
460 | info->has_toc = true; | ||
461 | memcpy( info->toc, vbrheader+i, 100 ); | ||
462 | i += 100; | ||
463 | } | ||
464 | if (vbrheader[7] & VBR_QUALITY_FLAG) | ||
465 | { | ||
466 | /* We don't care about this, but need to skip it */ | ||
467 | i += 4; | ||
468 | } | ||
469 | #if CONFIG_CODEC==SWCODEC | ||
470 | i += 21; | ||
471 | info->enc_delay = ((int)vbrheader[i ] << 4) | (vbrheader[i+1] >> 4); | ||
472 | info->enc_padding = ((int)vbrheader[i+1] << 8) | vbrheader[i+2]; | ||
473 | /* TODO: This sanity checking is rather silly, seeing as how the LAME | ||
474 | header contains a CRC field that can be used to verify integrity. */ | ||
475 | if (!(info->enc_delay >= 0 && info->enc_delay <= 2880 && | ||
476 | info->enc_padding >= 0 && info->enc_padding <= 2*1152)) | ||
477 | { | ||
478 | /* Invalid data */ | ||
479 | info->enc_delay = -1; | ||
480 | info->enc_padding = -1; | ||
481 | } | ||
482 | #endif | ||
483 | } | 518 | } |
484 | 519 | else if (!memcmp(vbrheader, "VBRI", 4)) | |
485 | if (!memcmp(vbrheader, "VBRI", 4)) | ||
486 | { | 520 | { |
487 | VDEBUGF("VBRI header\n"); | 521 | VDEBUGF("-- VBRI header --\n"); |
488 | 522 | ||
489 | /* We want to skip the VBRI frame when playing the stream */ | 523 | /* We want to skip the VBRI frame when playing the stream */ |
490 | bytecount += info->frame_size; | 524 | bytecount += info->frame_size; |
491 | 525 | ||
492 | /* Now get the next frame to find out the real info about | 526 | /* Now get the next frame to read the real info about the mp3 stream */ |
493 | the mp3 stream */ | 527 | result = get_next_header_info(fd, &bytecount, info); |
494 | header = find_next_frame(fd, &tmp, 0x20000, 0); | 528 | if(result) |
495 | if(header == 0) | 529 | return result; |
496 | return -6; | 530 | |
497 | 531 | get_vbri_info(info, vbrheader); | |
498 | bytecount += tmp; | 532 | } |
499 | 533 | else | |
500 | if(!mp3headerinfo(info, header)) | 534 | { |
501 | return -7; | 535 | VDEBUGF("-- No VBR header --\n"); |
502 | |||
503 | VDEBUGF("%04x: %04x %04x ", 0, (short)(header >> 16), | ||
504 | (short)(header & 0xffff)); | ||
505 | for(i = 4;i < (int)sizeof(frame)-4;i+=2) { | ||
506 | if(i % 16 == 0) { | ||
507 | VDEBUGF("\n%04x: ", i-4); | ||
508 | } | ||
509 | VDEBUGF("%04x ", (frame[i-4] << 8) | frame[i-4+1]); | ||
510 | } | ||
511 | |||
512 | VDEBUGF("\n"); | ||
513 | |||
514 | /* Yes, it is a FhG VBR file */ | ||
515 | info->is_vbr = true; | ||
516 | /* Rockbox: not used | ||
517 | info->is_vbri_vbr = true; | ||
518 | */ | ||
519 | info->has_toc = false; /* We don't parse the TOC (yet) */ | ||
520 | |||
521 | info->byte_count = bytes2int(vbrheader[10], vbrheader[11], | ||
522 | vbrheader[12], vbrheader[13]); | ||
523 | info->frame_count = bytes2int(vbrheader[14], vbrheader[15], | ||
524 | vbrheader[16], vbrheader[17]); | ||
525 | if (info->frame_count <= ULONG_MAX / info->ft_num) | ||
526 | info->file_time = info->frame_count * info->ft_num / info->ft_den; | ||
527 | else | ||
528 | info->file_time = info->frame_count / info->ft_den * info->ft_num; | ||
529 | |||
530 | if (info->byte_count <= (ULONG_MAX/8)) | ||
531 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
532 | else | ||
533 | info->bitrate = info->byte_count / (info->file_time >> 3); | ||
534 | |||
535 | /* We don't parse the TOC, since we don't yet know how to (FIXME) */ | ||
536 | num_offsets = bytes2int(0, 0, vbrheader[18], vbrheader[19]); | ||
537 | VDEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n", | ||
538 | info->bitrate, info->frame_size, info->frame_size); | ||
539 | VDEBUGF("Frame count: %lx\n", info->frame_count); | ||
540 | VDEBUGF("Byte count: %lx\n", info->byte_count); | ||
541 | VDEBUGF("Offsets: %d\n", num_offsets); | ||
542 | VDEBUGF("Frames/entry: %ld\n", | ||
543 | bytes2int(0, 0, vbrheader[24], vbrheader[25])); | ||
544 | |||
545 | offset = 0; | ||
546 | |||
547 | for(i = 0;i < num_offsets;i++) | ||
548 | { | ||
549 | j = bytes2int(0, 0, vbrheader[26+i*2], vbrheader[27+i*2]); | ||
550 | offset += j; | ||
551 | VDEBUGF("%03d: %lx (%x)\n", i, offset - bytecount, j); | ||
552 | } | ||
553 | } | 536 | } |
554 | 537 | ||
555 | return bytecount; | 538 | return bytecount; |