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
|
||||
|
||||
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];
|
||||
unsigned char *vbrheader;
|
||||
unsigned long header;
|
||||
long bytecount;
|
||||
int num_offsets;
|
||||
int i;
|
||||
long offset;
|
||||
int j;
|
||||
long tmp;
|
||||
int i = 8;
|
||||
|
||||
header = find_next_frame(fd, &bytecount, 0x20000, 0);
|
||||
/* Quit if we haven't found a valid header within 128K */
|
||||
/* Is it a VBR file? */
|
||||
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)
|
||||
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))
|
||||
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 */
|
||||
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)
|
||||
return -3;
|
||||
|
||||
/* calculate position of VBR header */
|
||||
if ( info->version == MPEG_VERSION1 ) {
|
||||
/* Calculate position of a possible VBR header */
|
||||
if (info->version == MPEG_VERSION1) {
|
||||
if (info->channel_mode == 3) /* mono */
|
||||
vbrheader = frame + 17;
|
||||
else
|
||||
vbrheader = frame + 32;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (info->channel_mode == 3) /* mono */
|
||||
vbrheader = frame + 9;
|
||||
else
|
||||
vbrheader = frame + 17;
|
||||
}
|
||||
|
||||
if (!memcmp(vbrheader, "Xing", 4)
|
||||
|| !memcmp(vbrheader, "Info", 4))
|
||||
if (!memcmp(vbrheader, "Xing", 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 */
|
||||
bytecount += info->frame_size;
|
||||
|
||||
/* Now get the next frame to find out the real info about
|
||||
the mp3 stream */
|
||||
header = find_next_frame(fd, &tmp, 0x20000, 0);
|
||||
if(header == 0)
|
||||
return -4;
|
||||
|
||||
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
|
||||
/* Now get the next frame to read the real info about the mp3 stream */
|
||||
result = get_next_header_info(fd, &bytecount, info);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
get_xing_info(info, vbrheader);
|
||||
}
|
||||
|
||||
if (!memcmp(vbrheader, "VBRI", 4))
|
||||
else if (!memcmp(vbrheader, "VBRI", 4))
|
||||
{
|
||||
VDEBUGF("VBRI header\n");
|
||||
VDEBUGF("-- VBRI header --\n");
|
||||
|
||||
/* We want to skip the VBRI frame when playing the stream */
|
||||
bytecount += info->frame_size;
|
||||
|
||||
/* Now get the next frame to find out the real info about
|
||||
the mp3 stream */
|
||||
header = find_next_frame(fd, &tmp, 0x20000, 0);
|
||||
if(header == 0)
|
||||
return -6;
|
||||
|
||||
bytecount += tmp;
|
||||
|
||||
if(!mp3headerinfo(info, header))
|
||||
return -7;
|
||||
|
||||
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);
|
||||
}
|
||||
/* Now get the next frame to read the real info about the mp3 stream */
|
||||
result = get_next_header_info(fd, &bytecount, info);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
get_vbri_info(info, vbrheader);
|
||||
}
|
||||
else
|
||||
{
|
||||
VDEBUGF("-- No VBR header --\n");
|
||||
}
|
||||
|
||||
return bytecount;
|
||||
|
|
Loading…
Reference in a new issue