[Rhythmbox-devel] better mp3 metadata
- From: Bastien Nocera <hadess hadess net>
- To: Rhythmbox Dev <rhythmbox-devel gnome org>
- Subject: [Rhythmbox-devel] better mp3 metadata
- Date: Fri, 14 Nov 2003 21:34:09 +0000
Hey,
So the old code was very broken, and slow. Now, it's fast, and should be
more correct as well :)
- Ported from xine-lib to qahog to rhythmbox
- Pretty much self-container (see mp3bitrate.[ch])
- Detects MPEG 2.5 files
- Doesn't use crappy magic numbers for files with Xing headers
- Get the time right for VBR files
Needs testing on big endian machines (but should work fine).
Let me know what you think, and please, commit that :)
---
Bastien Nocera <hadess@hadess.net>
Her artistic sense was exquisitely refined, like someone who can tell
butter from "I Can't Believe It's Not Butter."
Index: mp3-stream-info-impl.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/monkey-media/stream-info-impl/mp3-stream-info-impl.c,v
retrieving revision 1.6
diff -u -r1.6 mp3-stream-info-impl.c
--- mp3-stream-info-impl.c 3 Sep 2003 01:27:33 -0000 1.6
+++ mp3-stream-info-impl.c 14 Nov 2003 21:30:11 -0000
@@ -53,10 +53,21 @@
static int MP3_stream_info_impl_get_n_values (MonkeyMediaStreamInfo *info,
MonkeyMediaStreamInfoField field);
+struct MP3BitrateInfo
+{
+ int bitrate;
+ int samplerate;
+ int time;
+ int channels;
+ int version;
+ int vbr;
+};
+
struct MP3StreamInfoImplPrivate
{
struct id3_tag *tag;
struct id3_vfs_file *file;
+ struct MP3BitrateInfo *info_num;
};
static GObjectClass *parent_class = NULL;
@@ -125,7 +136,8 @@
if (impl->priv->file != NULL)
id3_vfs_close (impl->priv->file);
-
+
+ g_free (impl->priv->info_num);
g_free (impl->priv);
G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -157,6 +169,25 @@
impl->priv->tag = id3_vfs_tag (impl->priv->file);
}
+static void
+MP3_stream_info_impl_get_bitrate_info (MP3StreamInfoImpl *info)
+{
+ if (info->priv->info_num == NULL)
+ {
+ struct MP3BitrateInfo *info_num;
+
+ info_num = g_new0 (struct MP3BitrateInfo, 1);
+ id3_vfs_bitrate (info->priv->file,
+ &info_num->bitrate,
+ &info_num->samplerate,
+ &info_num->time,
+ &info_num->version,
+ &info_num->vbr,
+ &info_num->channels);
+ info->priv->info_num = info_num;
+ }
+}
+
static int
MP3_stream_info_impl_get_n_values (MonkeyMediaStreamInfo *info,
MonkeyMediaStreamInfoField field)
@@ -435,13 +466,14 @@
}
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_DURATION:
+ g_value_init (value, G_TYPE_LONG);
+ MP3_stream_info_impl_get_bitrate_info (impl);
+
+ if (impl->priv->info_num->vbr == 0)
{
- int bitrate;
GnomeVFSFileSize size;
GValue val = { 0, };
- g_value_init (value, G_TYPE_LONG);
-
MP3_stream_info_impl_get_value (info,
MONKEY_MEDIA_STREAM_INFO_FIELD_FILE_SIZE,
0,
@@ -449,12 +481,12 @@
size = g_value_get_long (&val);
g_value_unset (&val);
- bitrate = id3_vfs_bitrate (impl->priv->file);
-
- if (bitrate > 0)
- g_value_set_long (value, ((double) size / 1024.0f) / ((double) bitrate / 8.0f));
+ if (impl->priv->info_num->bitrate > 0)
+ g_value_set_long (value, ((double) size / 1000.0f) / ((double) impl->priv->info_num->bitrate / 8.0f / 1000.0f));
else
g_value_set_long (value, 0);
+ } else {
+ g_value_set_long (value, impl->priv->info_num->time);
}
break;
@@ -464,22 +496,27 @@
g_value_set_boolean (value, TRUE);
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_CODEC_INFO:
+ MP3_stream_info_impl_get_bitrate_info (impl);
+ g_value_init (value, G_TYPE_STRING);
+ if (impl->priv->info_num->version != 3)
{
- int version = id3_vfs_version (impl->priv->file);
- tmp = g_strdup_printf (_("MPEG %d Layer III"), version);
- g_value_init (value, G_TYPE_STRING);
- g_value_set_string (value, tmp);
- g_free (tmp);
+ tmp = g_strdup_printf (_("MPEG %d Layer III"), impl->priv->info_num->version);
+ } else {
+ tmp = g_strdup (_("MPEG 2.5 Layer III"));
}
+ g_value_set_string (value, tmp);
+ g_free (tmp);
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_BIT_RATE:
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_AVERAGE_BIT_RATE:
+ MP3_stream_info_impl_get_bitrate_info (impl);
g_value_init (value, G_TYPE_INT);
- g_value_set_int (value, id3_vfs_bitrate (impl->priv->file));
+ g_value_set_int (value, impl->priv->info_num->bitrate / 1000);
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_QUALITY:
+ MP3_stream_info_impl_get_bitrate_info (impl);
g_value_init (value, MONKEY_MEDIA_TYPE_AUDIO_QUALITY);
- g_value_set_enum (value, monkey_media_audio_quality_from_bit_rate (id3_vfs_bitrate (impl->priv->file)));
+ g_value_set_enum (value, monkey_media_audio_quality_from_bit_rate (impl->priv->info_num->bitrate / 1000));
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_TRM_ID:
/* FIXME */
@@ -487,16 +524,19 @@
g_value_set_string (value, NULL);
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_VARIABLE_BIT_RATE:
+ MP3_stream_info_impl_get_bitrate_info (impl);
g_value_init (value, G_TYPE_BOOLEAN);
- g_value_set_boolean (value, id3_vfs_vbr (impl->priv->file));
+ g_value_set_boolean (value, impl->priv->info_num->vbr);
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_SAMPLE_RATE:
+ MP3_stream_info_impl_get_bitrate_info (impl);
g_value_init (value, G_TYPE_LONG);
- g_value_set_long (value, id3_vfs_samplerate (impl->priv->file));
+ g_value_set_long (value, impl->priv->info_num->samplerate);
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_CHANNELS:
+ MP3_stream_info_impl_get_bitrate_info (impl);
g_value_init (value, G_TYPE_INT);
- g_value_set_int (value, id3_vfs_channels (impl->priv->file));
+ g_value_set_int (value, impl->priv->info_num->channels);
break;
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_SERIAL_NUMBER:
g_value_init (value, G_TYPE_LONG);
Index: id3-vfs/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/monkey-media/stream-info-impl/id3-vfs/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- id3-vfs/Makefile.am 1 Sep 2003 04:20:58 -0000 1.3
+++ id3-vfs/Makefile.am 14 Nov 2003 21:30:11 -0000
@@ -3,7 +3,7 @@
if USE_ID3VFS
noinst_LTLIBRARIES = libid3-vfs.la
-libid3_vfs_la_SOURCES = id3-vfs.c id3-vfs.h field.h file.h global.h tag.h
+libid3_vfs_la_SOURCES = id3-vfs.c id3-vfs.h field.h file.h global.h tag.h mp3bitrate.c mp3bitrate.h
libid3_vfs_la_LIBADD = $(ID3TAG_LIBS)
Index: id3-vfs/id3-vfs.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/monkey-media/stream-info-impl/id3-vfs/id3-vfs.c,v
retrieving revision 1.3
diff -u -r1.3 id3-vfs.c
--- id3-vfs/id3-vfs.c 1 Sep 2003 04:20:59 -0000 1.3
+++ id3-vfs/id3-vfs.c 14 Nov 2003 21:30:12 -0000
@@ -43,6 +43,7 @@
# include "file.h"
# include "tag.h"
# include "field.h"
+# include "mp3bitrate.h"
struct vfstag {
struct id3_tag *tag;
@@ -457,260 +458,64 @@
return 0;
}
-int bitrates[2][16] = {
-{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, /* MPEG2 */
-{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,}}; /* MPEG1 */
-
-long samprates[2][3] = {
-{ 22050, 24000, 16000 }, /* MPEG2 */
-{ 44100, 48000, 32000 }}; /* MPEG1 */
-
-static int extractI4(unsigned char *buf)
-{
- int x;
- /* big endian extract */
-
- x = buf[0];
- x <<= 8;
- x |= buf[1];
- x <<= 8;
- x |= buf[2];
- x <<= 8;
- x |= buf[3];
-
- return(x);
-}
-
-/* check for valid MPEG header */
-static int is_mphead(unsigned char *buf)
-{
- if (buf[0] != 0xff) return(0);
- if ((buf[1] & 0xf0) != 0xf0) return(0); /* 12 bits framesync */
-
- return(1);
-}
-
-/* check for valid "Xing" VBR header */
-static int is_xhead(unsigned char *buf)
-{
- if (buf[0] != 'X') return(0);
- if (buf[1] != 'i') return(0);
- if (buf[2] != 'n') return(0);
- if (buf[3] != 'g') return(0);
-
- return(1);
-}
-
-int id3_vfs_bitrate (struct id3_vfs_file *file)
+int
+id3_vfs_bitrate (struct id3_vfs_file *file, int *bitrate, int *samplerate,
+ int *time, int *version, int *vbr, int *channels)
{
- GnomeVFSFileSize save_position;
+ GnomeVFSFileSize save_position, length_read;
GnomeVFSHandle *iofile = file->iofile;
GnomeVFSResult res;
- int bitrate = 0;
- GnomeVFSFileSize length_read;
- guchar buffer[8192];
-
- if (gnome_vfs_tell(iofile, &save_position) != GNOME_VFS_OK)
- return 0;
-
- gnome_vfs_seek (iofile, GNOME_VFS_SEEK_START, 0);
+ guchar buffer[8192];
+ int found, i;
- res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
- if ((res == GNOME_VFS_OK) && (length_read > 512))
- {
- int i = 0;
- int ver,srindex,brindex,xbytes,xframes;
- long long total_bytes, magic1, magic2;
-
- while(!is_mphead(buffer+i)) {
- i++;
- if (i>length_read-4) goto bitdone; /* no valid header, give up */
- }
-
- ver = (buffer[i+1] & 0x08) >> 3;
- brindex = (buffer[i+2] & 0xf0) >> 4;
- srindex = (buffer[i+2] & 0x0c) >> 2;
-
- /* but if there is a Xing header we'll use that instead... */
- i=0;
- while(!is_xhead(buffer+i)) {
- i++;
- if (i>length_read-16)
- {
- bitrate = (bitrates[ver][brindex]);
- goto bitdone;
- }
- }
-
- xframes = extractI4(buffer+i+8);
- xbytes = extractI4(buffer+i+12);
-
- if (xframes <= 0) {
- bitrate = 0;
- goto bitdone;
- }
-
- total_bytes = (long long) samprates[ver][srindex] * (long long) xbytes;
- magic1 = total_bytes / (long long) (576 + ver * 576);
- magic2 = magic1 / (long long) xframes;
- bitrate = (int) ((long long) magic2 / (long long) 125);
- }
-
-bitdone:
- if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position)
- != GNOME_VFS_OK)
- return 0;
-
- return bitrate;
-}
-
-long id3_vfs_samplerate (struct id3_vfs_file *file)
-{
- GnomeVFSFileSize save_position;
- GnomeVFSHandle *iofile = file->iofile;
- GnomeVFSResult res;
- long samprate = 0;
- GnomeVFSFileSize length_read;
- guchar buffer[8192];
+ *bitrate = 0;
+ *samplerate = 0;
+ *time = 0;
+ *channels = 0;
+ *version = 0;
+ *vbr = 0;
+ found = 0;
if (gnome_vfs_tell(iofile, &save_position) != GNOME_VFS_OK)
return 0;
- gnome_vfs_seek (iofile, GNOME_VFS_SEEK_START, 0);
+ gnome_vfs_seek (iofile, GNOME_VFS_SEEK_START, 0);
res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
- if ((res == GNOME_VFS_OK) && (length_read > 512))
- {
- int i = 0;
- int ver,srindex;
-
- while(!is_mphead(buffer+i)) {
- i++;
- if (i>length_read-4) goto sampdone; /* no valid header, give up */
- }
-
- ver = (buffer[i+1] & 0x08) >> 3;
- srindex = (buffer[i+2] & 0x0c) >> 2;
+ if( res != GNOME_VFS_OK || length_read < 512 )
+ goto bitdone;
- samprate = (long) (samprates[ver][srindex]);
- }
-
-sampdone:
- if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position)
- != GNOME_VFS_OK)
- return 0;
-
- return samprate;
-}
-
-int id3_vfs_channels (struct id3_vfs_file *file)
-{
- GnomeVFSFileSize save_position;
- GnomeVFSHandle *iofile = file->iofile;
- GnomeVFSResult res;
- int channels = 0;
- GnomeVFSFileSize length_read;
- guchar buffer[8192];
-
- if (gnome_vfs_tell(iofile, &save_position) != GNOME_VFS_OK)
- return 0;
-
- gnome_vfs_seek (iofile, GNOME_VFS_SEEK_START, 0);
-
- res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
- if ((res == GNOME_VFS_OK) && (length_read > 512))
+ for (i = 0; i + 4 < length_read; i++)
{
- int i = 0;
-
- while(!is_mphead(buffer+i)) {
- i++;
- if (i>length_read-4) goto chandone; /* no valid header, give up */
- }
-
- channels = (((buffer[i+3] & 0xc0) >> 6) == 2) ? 1 : 2;
+ if (mp3_bitrate_parse_header (buffer+i, length_read - i, bitrate, samplerate, time, version, vbr, channels))
+ {
+ found = 1;
+ break;
+ }
}
-chandone:
- if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position)
- != GNOME_VFS_OK)
- return 0;
-
- return channels;
-}
-
-gboolean id3_vfs_vbr (struct id3_vfs_file *file)
-{
- GnomeVFSFileSize save_position;
- GnomeVFSHandle *iofile = file->iofile;
- GnomeVFSResult res;
- gboolean vbr = FALSE;
- GnomeVFSFileSize length_read;
- guchar buffer[8192];
-
- if (gnome_vfs_tell(iofile, &save_position) != GNOME_VFS_OK)
- return 0;
-
- gnome_vfs_seek (iofile, GNOME_VFS_SEEK_START, 0);
-
- res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
- if ((res == GNOME_VFS_OK) && (length_read > 512))
+ /* If we haven't found anything, try again with 8 more kB */
+ if (found == 0)
{
- int i = 0;
-
- while(!is_mphead(buffer+i)) {
- i++;
- if (i>length_read-4) goto vbrdone; /* no valid header, give up */
- }
-
- /* but if there is a Xing header we'll use that instead... */
- i=0;
- while(!is_xhead(buffer+i)) {
- i++;
- if (i>length_read-16) goto vbrdone;
- }
-
- vbr = TRUE;
- }
+ res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
-vbrdone:
- if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position)
- != GNOME_VFS_OK)
- return 0;
-
- return vbr;
-}
-
-int id3_vfs_version (struct id3_vfs_file *file)
-{
- GnomeVFSFileSize save_position;
- GnomeVFSHandle *iofile = file->iofile;
- GnomeVFSResult res;
- int version = 0;
- GnomeVFSFileSize length_read;
- guchar buffer[8192];
-
- if (gnome_vfs_tell(iofile, &save_position) != GNOME_VFS_OK)
- return 0;
+ if( res != GNOME_VFS_OK || length_read < 512 )
+ goto bitdone;
- gnome_vfs_seek (iofile, GNOME_VFS_SEEK_START, 0);
-
- res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
- if ((res == GNOME_VFS_OK) && (length_read > 512))
- {
- int i = 0;
-
- while(!is_mphead(buffer+i)) {
- i++;
- if (i>length_read-4) goto verdone; /* no valid header, give up */
- }
-
- version = (buffer[i+1] & 0x08) >> 3;
+ for (i = 0; i + 4 < length_read; i++)
+ {
+ if (mp3_bitrate_parse_header (buffer+i, length_read - i, bitrate, samplerate, time, version, vbr, channels))
+ {
+ found = 1;
+ break;
+ }
+ }
}
-verdone:
- if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position)
- != GNOME_VFS_OK)
+bitdone:
+ if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position) != GNOME_VFS_OK)
return 0;
- return version;
+ return 1;
}
+
Index: id3-vfs/id3-vfs.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/monkey-media/stream-info-impl/id3-vfs/id3-vfs.h,v
retrieving revision 1.2
diff -u -r1.2 id3-vfs.h
--- id3-vfs/id3-vfs.h 1 Sep 2003 04:20:59 -0000 1.2
+++ id3-vfs/id3-vfs.h 14 Nov 2003 21:30:12 -0000
@@ -37,11 +37,8 @@
struct id3_tag *id3_vfs_tag(struct id3_vfs_file const *);
int id3_vfs_update(struct id3_vfs_file *);
-int id3_vfs_bitrate(struct id3_vfs_file *);
-long id3_vfs_samplerate(struct id3_vfs_file *);
-int id3_vfs_channels(struct id3_vfs_file *);
-gboolean id3_vfs_vbr(struct id3_vfs_file *);
-int id3_vfs_version(struct id3_vfs_file *);
+int id3_vfs_bitrate (struct id3_vfs_file *file, int *bitrate, int *samplerate,
+ int *time, int *version, int *vbr, int *channels);
#endif /* _ID3_VFS_H_ */
Index: id3-vfs/mp3bitrate.c
===================================================================
RCS file: id3-vfs/mp3bitrate.c
diff -N id3-vfs/mp3bitrate.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ id3-vfs/mp3bitrate.c 14 Nov 2003 21:30:13 -0000
@@ -0,0 +1,259 @@
+
+#include "config.h"
+
+#include <glib.h>
+#include "mp3bitrate.h"
+
+#undef LOG
+#ifdef LOG
+#define lprintf(x...) g_print(x)
+#else
+#define lprintf(x...)
+#endif
+
+/* bitrate table tabsel_123[mpeg version][layer][bitrate index]
+ * values stored in kbps
+ */
+const int tabsel_123[2][3][16] = {
+ { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
+ {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
+ {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} },
+
+ { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},
+ {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,},
+ {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,} }
+};
+
+static int frequencies[3][3] = {
+ { 44100, 48000, 32000 },
+ { 22050, 24000, 16000 },
+ { 11025, 12000, 8000 }
+};
+
+typedef struct
+{
+ int mpeg25_bit;
+ int layer;
+ int channel_mode;
+ int lsf_bit;
+} MP3Frame;
+
+#ifndef WORDS_BIGENDIAN
+#define BE_32(x) ((((guint8*)(x))[0] << 24) | \
+ (((guint8*)(x))[1] << 16) | \
+ (((guint8*)(x))[2] << 8) | \
+ ((guint8*)(x))[3])
+#else
+#define BE_32(x) (*(guint32 *) (x))
+#endif
+
+#define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
+ ( (long)(unsigned char)(ch3) | \
+ ( (long)(unsigned char)(ch2) << 8 ) | \
+ ( (long)(unsigned char)(ch1) << 16 ) | \
+ ( (long)(unsigned char)(ch0) << 24 ) )
+
+#define XING_TAG FOURCC_TAG('X', 'i', 'n', 'g')
+#define XING_FRAMES_FLAG 0x0001
+#define XING_BYTES_FLAG 0x0002
+#define XING_TOC_FLAG 0x0004
+#define XING_VBR_SCALE_FLAG 0x0008
+
+/* check for valid "Xing" VBR header */
+static int is_xhead(unsigned char *buf)
+{
+ return (BE_32(buf) == XING_TAG);
+}
+
+static int mpg123_parse_xing_header(MP3Frame *frame,
+ int samplerate,
+ guint8 *buf, int bufsize, int *bitrate,
+ int *time)
+{
+ int i;
+ guint8 *ptr = buf;
+ double frame_duration;
+ int xflags, xframes, xbytes, xvbr_scale;
+ int abr;
+ guint8 xtoc[100];
+
+ xframes = xbytes = 0;
+
+ /* offset of the Xing header */
+ if( frame->mpeg25_bit )
+ {
+ /* mpeg1 */
+ if( frame->channel_mode != 3 )
+ ptr += (32 + 4);
+ else
+ ptr += (17 + 4);
+ } else {
+ /* mpeg2 */
+ if( frame->channel_mode != 3 )
+ ptr += (17 + 4);
+ else
+ ptr += (9 + 4);
+ }
+
+ if (ptr >= (buf + bufsize))
+ return 0;
+
+ if (is_xhead(ptr))
+ {
+ lprintf("Xing header found\n");
+
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+
+ xflags = BE_32(ptr);
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+
+ if (xflags & XING_FRAMES_FLAG)
+ {
+ xframes = BE_32(ptr);
+ lprintf("xframes: %d\n", xframes);
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+ }
+ if (xflags & XING_BYTES_FLAG)
+ {
+ xbytes = BE_32(ptr);
+ lprintf("xbytes: %d\n", xbytes);
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+ }
+ if (xflags & XING_TOC_FLAG)
+ {
+ lprintf("toc found\n");
+ for (i = 0; i < 100; i++)
+ {
+ xtoc[i] = *(ptr + i);
+ lprintf("%d ", xtoc[i]);
+ }
+ lprintf("\n");
+ }
+ ptr += 100; if (ptr >= (buf + bufsize)) return 0;
+ xvbr_scale = -1;
+ if (xflags & XING_VBR_SCALE_FLAG) {
+ xvbr_scale = BE_32(ptr);
+ lprintf("xvbr_scale: %d\n", xvbr_scale);
+ }
+
+ /* 1 kbit = 1000 bits ! (and not 1024 bits) */
+ if (xflags & (XING_FRAMES_FLAG | XING_BYTES_FLAG)) {
+ if (frame->layer == 1) {
+ frame_duration = 384.0 / (double)samplerate;
+ } else {
+ int slots_per_frame;
+ slots_per_frame = (frame->layer == 3 && !frame->lsf_bit) ? 72 : 144;
+ frame_duration = slots_per_frame * 8.0 / (double)samplerate;
+ }
+ abr = ((double)xbytes * 8.0) / ((double)xframes * frame_duration);
+ lprintf("abr: %d bps\n", abr);
+ *bitrate = abr;
+ *time = (double)xframes * frame_duration;
+ lprintf("stream_length: %d s, %d min %d s\n", *time,
+ *time / 60, *time % 60);
+ } else {
+ /* it's a stupid Xing header */
+ lprintf ("not a Xing VBR file\n");
+ }
+ return 1;
+ } else {
+ lprintf("Xing header not found\n");
+ return 0;
+ }
+}
+
+/*
+ * Returns 1 if the header was parsed successfully, 0 if it failed
+ *
+ * bitrate: self-explanatory
+ * samplerate: ditto
+ * time: only informed if we have a VBR file with Xing headers, needs to be
+ * deduced from the bitrate and filesize otherwise
+ * version: 1 for MPEG Version 1, 2 for MPEG Version 2, and
+ * 3 for MPEG Version 2.5
+ * vbr: whether it is a variable bitrate stream
+ */
+int
+mp3_bitrate_parse_header (guchar *buffer, guint length_read, int *bitrate, int *samplerate, int *time, int *version, int *vbr, int *channels)
+{
+ guint32 head;
+ int i = 0;
+ MP3Frame frame;
+ int bitrate_idx, version_idx, freq_idx, frame_sync;
+
+ head = BE_32(buffer);
+ lprintf ("buffer2: %08X\n", head);
+
+ frame_sync = head >> 21;
+ if (frame_sync != 0x7ff)
+ {
+ lprintf ("invalid frame sync\n");
+ return 0;
+ }
+ /* Magic to detect MPEG version 2.5 */
+ frame.mpeg25_bit = (head >> 20) & 0x1;
+ frame.lsf_bit = (head >> 19) & 0x1;
+ if (!frame.mpeg25_bit)
+ {
+ if (frame.lsf_bit)
+ {
+ lprintf("reserved mpeg25 lsf combination\n");
+ return 0;
+ } else
+ version_idx = 2; /* MPEG Version 2.5 */
+ } else {
+ if (!frame.lsf_bit)
+ version_idx = 1; /* MPEG Version 2 */
+ else
+ version_idx = 0; /* MPEG Version 1 */
+ }
+ lprintf ("version_idx %d\n", version_idx);
+ *version = version_idx + 1;
+
+ frame.layer = 4 - ((head >> 17) & 0x3);
+ if (frame.layer == 4)
+ {
+ lprintf("reserved layer\n");
+ return 0;
+ }
+ lprintf ("layer %d\n", frame.layer);
+
+ bitrate_idx = (head >> 12) & 0xf;
+ if ((bitrate_idx == 0) || (bitrate_idx == 15))
+ {
+ lprintf("invalid bitrate index\n");
+ return 0;
+ }
+ lprintf ("bitrate_idx %d\n", bitrate_idx);
+
+ freq_idx = (head >> 10) & 0x3;
+ if (freq_idx == 3) {
+ lprintf("invalid frequence index\n");
+ return 0;
+ }
+ lprintf ("freq_idx %d\n", freq_idx);
+
+ /* 0: Stereo
+ * 1: Joint Stereo
+ * 2: Dual Stereo
+ * 3: Mono */
+ frame.channel_mode = (head >> 6) & 0x3;
+
+ *bitrate = tabsel_123[!frame.lsf_bit][frame.layer - 1][bitrate_idx] * 1000;
+ *samplerate = frequencies[version_idx][freq_idx];
+ *channels = (frame.channel_mode == 3 ? 1 : 2);
+ lprintf ("frequencies[%d][%d]", version_idx, freq_idx);
+
+ for( i = 0; i + 4 < length_read; i++)
+ {
+ if( mpg123_parse_xing_header (&frame, *samplerate,
+ buffer+i, length_read-i, bitrate, time ) )
+ {
+ *vbr = 1;
+ break;
+ }
+ }
+
+ return 1;
+}
+
Index: id3-vfs/mp3bitrate.h
===================================================================
RCS file: id3-vfs/mp3bitrate.h
diff -N id3-vfs/mp3bitrate.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ id3-vfs/mp3bitrate.h 14 Nov 2003 21:30:13 -0000
@@ -0,0 +1,18 @@
+
+/*
+ * Returns 1 if the header was parsed successfully, 0 if it failed
+ *
+ * bitrate: in bps, not kbps
+ * samplerate: ditto
+ * time: only informed if we have a VBR file with Xing headers, needs to be
+ * deduced from the bitrate and filesize otherwise, in seconds
+ * version: 1 for MPEG Version 1, 2 for MPEG Version 2, and
+ * 3 for MPEG Version 2.5
+ * vbr: whether it is a variable bitrate stream
+ * channels: number of channels used in the stream
+ */
+
+int mp3_bitrate_parse_header (guchar *buffer, guint length_read, int *bitrate,
+ int *samplerate, int *time, int *version, int *vbr,
+ int *channels);
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]