mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-11-14 23:52:26 -05:00
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
This commit is contained in:
parent
92183d2dd0
commit
ff1b2b7fab
1 changed files with 145 additions and 162 deletions
307
apps/mp3data.c
307
apps/mp3data.c
|
|
@ -349,32 +349,135 @@ unsigned long mem_find_next_frame(int startpos, long *offset, long max_offset,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int get_mp3file_info(int fd, struct mp3info *info)
|
/* Extract information from a 'Xing' or 'Info' header. */
|
||||||
|
static void get_xing_info(struct mp3info *info, unsigned char *buf)
|
||||||
{
|
{
|
||||||
unsigned char frame[1800];
|
int i = 8;
|
||||||
unsigned char *vbrheader;
|
|
||||||
unsigned long header;
|
|
||||||
long bytecount;
|
|
||||||
int num_offsets;
|
|
||||||
int i;
|
|
||||||
long offset;
|
|
||||||
int j;
|
|
||||||
long tmp;
|
|
||||||
|
|
||||||
header = find_next_frame(fd, &bytecount, 0x20000, 0);
|
/* Is it a VBR file? */
|
||||||
/* Quit if we haven't found a valid header within 128K */
|
info->is_vbr = !memcmp(buf, "Xing", 4);
|
||||||
|
|
||||||
|
if (buf[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */
|
||||||
|
{
|
||||||
|
info->frame_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]);
|
||||||
|
if (info->frame_count <= ULONG_MAX / info->ft_num)
|
||||||
|
info->file_time = info->frame_count * info->ft_num / info->ft_den;
|
||||||
|
else
|
||||||
|
info->file_time = info->frame_count / info->ft_den * info->ft_num;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf[7] & VBR_BYTES_FLAG) /* Is byte count there? */
|
||||||
|
{
|
||||||
|
info->byte_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]);
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->file_time && info->byte_count)
|
||||||
|
{
|
||||||
|
if (info->byte_count <= (ULONG_MAX/8))
|
||||||
|
info->bitrate = info->byte_count * 8 / info->file_time;
|
||||||
|
else
|
||||||
|
info->bitrate = info->byte_count / (info->file_time >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */
|
||||||
|
{
|
||||||
|
info->has_toc = true;
|
||||||
|
memcpy( info->toc, buf+i, 100 );
|
||||||
|
i += 100;
|
||||||
|
}
|
||||||
|
if (buf[7] & VBR_QUALITY_FLAG)
|
||||||
|
{
|
||||||
|
/* We don't care about this, but need to skip it */
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
#if CONFIG_CODEC==SWCODEC
|
||||||
|
i += 21;
|
||||||
|
info->enc_delay = ((int)buf[i ] << 4) | (buf[i+1] >> 4);
|
||||||
|
info->enc_padding = ((int)buf[i+1] << 8) | buf[i+2];
|
||||||
|
/* TODO: This sanity checking is rather silly, seeing as how the LAME
|
||||||
|
header contains a CRC field that can be used to verify integrity. */
|
||||||
|
if (!(info->enc_delay >= 0 && info->enc_delay <= 2880 &&
|
||||||
|
info->enc_padding >= 0 && info->enc_padding <= 2*1152))
|
||||||
|
{
|
||||||
|
/* Invalid data */
|
||||||
|
info->enc_delay = -1;
|
||||||
|
info->enc_padding = -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract information from a 'VBRI' header. */
|
||||||
|
static void get_vbri_info(struct mp3info *info, unsigned char *buf)
|
||||||
|
{
|
||||||
|
int i, num_offsets, offset = 0;
|
||||||
|
|
||||||
|
info->is_vbr = true; /* Yes, it is a FhG VBR file */
|
||||||
|
info->has_toc = false; /* We don't parse the TOC (yet) */
|
||||||
|
|
||||||
|
info->byte_count = bytes2int(buf[10], buf[11], buf[12], buf[13]);
|
||||||
|
info->frame_count = bytes2int(buf[14], buf[15], buf[16], buf[17]);
|
||||||
|
if (info->frame_count <= ULONG_MAX / info->ft_num)
|
||||||
|
info->file_time = info->frame_count * info->ft_num / info->ft_den;
|
||||||
|
else
|
||||||
|
info->file_time = info->frame_count / info->ft_den * info->ft_num;
|
||||||
|
|
||||||
|
if (info->byte_count <= (ULONG_MAX/8))
|
||||||
|
info->bitrate = info->byte_count * 8 / info->file_time;
|
||||||
|
else
|
||||||
|
info->bitrate = info->byte_count / (info->file_time >> 3);
|
||||||
|
|
||||||
|
/* We don't parse the TOC, since we don't yet know how to (FIXME) */
|
||||||
|
num_offsets = bytes2int(0, 0, buf[18], buf[19]);
|
||||||
|
VDEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n",
|
||||||
|
info->bitrate, info->frame_size, info->frame_size);
|
||||||
|
VDEBUGF("Frame count: %lx\n", info->frame_count);
|
||||||
|
VDEBUGF("Byte count: %lx\n", info->byte_count);
|
||||||
|
VDEBUGF("Offsets: %d\n", num_offsets);
|
||||||
|
VDEBUGF("Frames/entry: %ld\n",
|
||||||
|
bytes2int(0, 0, buf[24], buf[25]));
|
||||||
|
|
||||||
|
for(i = 0; i < num_offsets; i++)
|
||||||
|
{
|
||||||
|
offset += bytes2int(0, 0, buf[26+i*2], buf[27+i*2]);;
|
||||||
|
VDEBUGF("%03d: %lx\n", i, offset - bytecount,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seek to next mpeg header and extract relevant information. */
|
||||||
|
static int get_next_header_info(int fd, long *bytecount, struct mp3info *info)
|
||||||
|
{
|
||||||
|
unsigned long header = find_next_frame(fd, bytecount, 0x20000, 0);
|
||||||
if(header == 0)
|
if(header == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(info, 0, sizeof(struct mp3info));
|
|
||||||
memset(frame, 0, sizeof(frame));
|
|
||||||
#if CONFIG_CODEC==SWCODEC
|
|
||||||
/* These two are needed for proper LAME gapless MP3 playback */
|
|
||||||
info->enc_delay = -1;
|
|
||||||
info->enc_padding = -1;
|
|
||||||
#endif
|
|
||||||
if(!mp3headerinfo(info, header))
|
if(!mp3headerinfo(info, header))
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_mp3file_info(int fd, struct mp3info *info)
|
||||||
|
{
|
||||||
|
unsigned char frame[1800], *vbrheader;
|
||||||
|
long bytecount;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* Initialize info and frame */
|
||||||
|
memset(info, 0, sizeof(struct mp3info));
|
||||||
|
memset(frame, 0, sizeof(frame));
|
||||||
|
|
||||||
|
#if CONFIG_CODEC==SWCODEC
|
||||||
|
/* These two are needed for proper LAME gapless MP3 playback */
|
||||||
|
info->enc_delay = -1;
|
||||||
|
info->enc_padding = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Get the very first MPEG frame. */
|
||||||
|
result = get_next_header_info(fd, &bytecount, info);
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
|
||||||
/* OK, we have found a frame. Let's see if it has a Xing header */
|
/* OK, we have found a frame. Let's see if it has a Xing header */
|
||||||
if (info->frame_size-4 >= (int)sizeof(frame))
|
if (info->frame_size-4 >= (int)sizeof(frame))
|
||||||
|
|
@ -386,170 +489,50 @@ int get_mp3file_info(int fd, struct mp3info *info)
|
||||||
if(read(fd, frame, info->frame_size-4) < 0)
|
if(read(fd, frame, info->frame_size-4) < 0)
|
||||||
return -3;
|
return -3;
|
||||||
|
|
||||||
/* calculate position of VBR header */
|
/* Calculate position of a possible VBR header */
|
||||||
if ( info->version == MPEG_VERSION1 ) {
|
if (info->version == MPEG_VERSION1) {
|
||||||
if (info->channel_mode == 3) /* mono */
|
if (info->channel_mode == 3) /* mono */
|
||||||
vbrheader = frame + 17;
|
vbrheader = frame + 17;
|
||||||
else
|
else
|
||||||
vbrheader = frame + 32;
|
vbrheader = frame + 32;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (info->channel_mode == 3) /* mono */
|
if (info->channel_mode == 3) /* mono */
|
||||||
vbrheader = frame + 9;
|
vbrheader = frame + 9;
|
||||||
else
|
else
|
||||||
vbrheader = frame + 17;
|
vbrheader = frame + 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memcmp(vbrheader, "Xing", 4)
|
if (!memcmp(vbrheader, "Xing", 4) || !memcmp(vbrheader, "Info", 4))
|
||||||
|| !memcmp(vbrheader, "Info", 4))
|
|
||||||
{
|
{
|
||||||
int i = 8; /* Where to start parsing info */
|
VDEBUGF("-- XING header --\n");
|
||||||
|
|
||||||
/* DEBUGF("Xing/Info header\n"); */
|
|
||||||
|
|
||||||
/* Remember where in the file the Xing header is */
|
|
||||||
/* Rockbox: not used
|
|
||||||
info->vbr_header_pos = lseek(fd, 0, SEEK_CUR) - info->frame_size;
|
|
||||||
*/
|
|
||||||
/* We want to skip the Xing frame when playing the stream */
|
/* We want to skip the Xing frame when playing the stream */
|
||||||
bytecount += info->frame_size;
|
bytecount += info->frame_size;
|
||||||
|
|
||||||
/* Now get the next frame to find out the real info about
|
/* Now get the next frame to read the real info about the mp3 stream */
|
||||||
the mp3 stream */
|
result = get_next_header_info(fd, &bytecount, info);
|
||||||
header = find_next_frame(fd, &tmp, 0x20000, 0);
|
if(result)
|
||||||
if(header == 0)
|
return result;
|
||||||
return -4;
|
|
||||||
|
get_xing_info(info, vbrheader);
|
||||||
if(!mp3headerinfo(info, header))
|
|
||||||
return -5;
|
|
||||||
|
|
||||||
/* Is it a VBR file? */
|
|
||||||
info->is_vbr = !memcmp(vbrheader, "Xing", 4);
|
|
||||||
/* Rockbox: not used
|
|
||||||
info->is_xing_vbr = info->is_vbr;
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (vbrheader[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */
|
|
||||||
{
|
|
||||||
info->frame_count = bytes2int(vbrheader[i], vbrheader[i+1],
|
|
||||||
vbrheader[i+2], vbrheader[i+3]);
|
|
||||||
if (info->frame_count <= ULONG_MAX / info->ft_num)
|
|
||||||
info->file_time = info->frame_count * info->ft_num / info->ft_den;
|
|
||||||
else
|
|
||||||
info->file_time = info->frame_count / info->ft_den * info->ft_num;
|
|
||||||
i += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vbrheader[7] & VBR_BYTES_FLAG) /* Is byte count there? */
|
|
||||||
{
|
|
||||||
info->byte_count = bytes2int(vbrheader[i], vbrheader[i+1],
|
|
||||||
vbrheader[i+2], vbrheader[i+3]);
|
|
||||||
i += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info->file_time && info->byte_count)
|
|
||||||
{
|
|
||||||
if (info->byte_count <= (ULONG_MAX/8))
|
|
||||||
info->bitrate = info->byte_count * 8 / info->file_time;
|
|
||||||
else
|
|
||||||
info->bitrate = info->byte_count / (info->file_time >> 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vbrheader[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */
|
|
||||||
{
|
|
||||||
info->has_toc = true;
|
|
||||||
memcpy( info->toc, vbrheader+i, 100 );
|
|
||||||
i += 100;
|
|
||||||
}
|
|
||||||
if (vbrheader[7] & VBR_QUALITY_FLAG)
|
|
||||||
{
|
|
||||||
/* We don't care about this, but need to skip it */
|
|
||||||
i += 4;
|
|
||||||
}
|
|
||||||
#if CONFIG_CODEC==SWCODEC
|
|
||||||
i += 21;
|
|
||||||
info->enc_delay = ((int)vbrheader[i ] << 4) | (vbrheader[i+1] >> 4);
|
|
||||||
info->enc_padding = ((int)vbrheader[i+1] << 8) | vbrheader[i+2];
|
|
||||||
/* TODO: This sanity checking is rather silly, seeing as how the LAME
|
|
||||||
header contains a CRC field that can be used to verify integrity. */
|
|
||||||
if (!(info->enc_delay >= 0 && info->enc_delay <= 2880 &&
|
|
||||||
info->enc_padding >= 0 && info->enc_padding <= 2*1152))
|
|
||||||
{
|
|
||||||
/* Invalid data */
|
|
||||||
info->enc_delay = -1;
|
|
||||||
info->enc_padding = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
else if (!memcmp(vbrheader, "VBRI", 4))
|
||||||
if (!memcmp(vbrheader, "VBRI", 4))
|
|
||||||
{
|
{
|
||||||
VDEBUGF("VBRI header\n");
|
VDEBUGF("-- VBRI header --\n");
|
||||||
|
|
||||||
/* We want to skip the VBRI frame when playing the stream */
|
/* We want to skip the VBRI frame when playing the stream */
|
||||||
bytecount += info->frame_size;
|
bytecount += info->frame_size;
|
||||||
|
|
||||||
/* Now get the next frame to find out the real info about
|
/* Now get the next frame to read the real info about the mp3 stream */
|
||||||
the mp3 stream */
|
result = get_next_header_info(fd, &bytecount, info);
|
||||||
header = find_next_frame(fd, &tmp, 0x20000, 0);
|
if(result)
|
||||||
if(header == 0)
|
return result;
|
||||||
return -6;
|
|
||||||
|
get_vbri_info(info, vbrheader);
|
||||||
bytecount += tmp;
|
}
|
||||||
|
else
|
||||||
if(!mp3headerinfo(info, header))
|
{
|
||||||
return -7;
|
VDEBUGF("-- No VBR header --\n");
|
||||||
|
|
||||||
VDEBUGF("%04x: %04x %04x ", 0, (short)(header >> 16),
|
|
||||||
(short)(header & 0xffff));
|
|
||||||
for(i = 4;i < (int)sizeof(frame)-4;i+=2) {
|
|
||||||
if(i % 16 == 0) {
|
|
||||||
VDEBUGF("\n%04x: ", i-4);
|
|
||||||
}
|
|
||||||
VDEBUGF("%04x ", (frame[i-4] << 8) | frame[i-4+1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
VDEBUGF("\n");
|
|
||||||
|
|
||||||
/* Yes, it is a FhG VBR file */
|
|
||||||
info->is_vbr = true;
|
|
||||||
/* Rockbox: not used
|
|
||||||
info->is_vbri_vbr = true;
|
|
||||||
*/
|
|
||||||
info->has_toc = false; /* We don't parse the TOC (yet) */
|
|
||||||
|
|
||||||
info->byte_count = bytes2int(vbrheader[10], vbrheader[11],
|
|
||||||
vbrheader[12], vbrheader[13]);
|
|
||||||
info->frame_count = bytes2int(vbrheader[14], vbrheader[15],
|
|
||||||
vbrheader[16], vbrheader[17]);
|
|
||||||
if (info->frame_count <= ULONG_MAX / info->ft_num)
|
|
||||||
info->file_time = info->frame_count * info->ft_num / info->ft_den;
|
|
||||||
else
|
|
||||||
info->file_time = info->frame_count / info->ft_den * info->ft_num;
|
|
||||||
|
|
||||||
if (info->byte_count <= (ULONG_MAX/8))
|
|
||||||
info->bitrate = info->byte_count * 8 / info->file_time;
|
|
||||||
else
|
|
||||||
info->bitrate = info->byte_count / (info->file_time >> 3);
|
|
||||||
|
|
||||||
/* We don't parse the TOC, since we don't yet know how to (FIXME) */
|
|
||||||
num_offsets = bytes2int(0, 0, vbrheader[18], vbrheader[19]);
|
|
||||||
VDEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n",
|
|
||||||
info->bitrate, info->frame_size, info->frame_size);
|
|
||||||
VDEBUGF("Frame count: %lx\n", info->frame_count);
|
|
||||||
VDEBUGF("Byte count: %lx\n", info->byte_count);
|
|
||||||
VDEBUGF("Offsets: %d\n", num_offsets);
|
|
||||||
VDEBUGF("Frames/entry: %ld\n",
|
|
||||||
bytes2int(0, 0, vbrheader[24], vbrheader[25]));
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
|
|
||||||
for(i = 0;i < num_offsets;i++)
|
|
||||||
{
|
|
||||||
j = bytes2int(0, 0, vbrheader[26+i*2], vbrheader[27+i*2]);
|
|
||||||
offset += j;
|
|
||||||
VDEBUGF("%03d: %lx (%x)\n", i, offset - bytecount, j);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytecount;
|
return bytecount;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue