mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
gst/mpegaudioparse/gstmpegaudioparse.*: Add initial support for reading VBRI headers as found in VBR files created by...
Original commit message from CVS: * gst/mpegaudioparse/gstmpegaudioparse.c: (gst_mp3parse_reset), (gst_mp3parse_emit_frame), (gst_mp3parse_handle_first_frame), (mp3parse_total_bytes), (mp3parse_total_time): * gst/mpegaudioparse/gstmpegaudioparse.h: Add initial support for reading VBRI headers as found in VBR files created by some Fraunhofer encoders. Currently we only read the number of frames and bytes (and calculate duration, etc from this) but there is also a seek table that we currently don't use.
This commit is contained in:
parent
96205bedb5
commit
be2f3d1d99
3 changed files with 151 additions and 15 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
2008-01-14 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
|
||||
* gst/mpegaudioparse/gstmpegaudioparse.c: (gst_mp3parse_reset),
|
||||
(gst_mp3parse_emit_frame), (gst_mp3parse_handle_first_frame),
|
||||
(mp3parse_total_bytes), (mp3parse_total_time):
|
||||
* gst/mpegaudioparse/gstmpegaudioparse.h:
|
||||
Add initial support for reading VBRI headers as found in VBR files
|
||||
created by some Fraunhofer encoders. Currently we only read the
|
||||
number of frames and bytes (and calculate duration, etc from this)
|
||||
but there is also a seek table that we currently don't use.
|
||||
|
||||
2008-01-14 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
|
||||
Patch by: Mark Nauwelaerts <manauw at syknet dot be>
|
||||
|
|
|
@ -267,6 +267,17 @@ gst_mp3parse_reset (GstMPEGAudioParse * mp3parse)
|
|||
|
||||
mp3parse->xing_flags = 0;
|
||||
mp3parse->xing_bitrate = 0;
|
||||
mp3parse->xing_frames = 0;
|
||||
mp3parse->xing_total_time = 0;
|
||||
mp3parse->xing_bytes = 0;
|
||||
mp3parse->xing_vbr_scale = 0;
|
||||
memset (mp3parse->xing_seek_table, 0, 100);
|
||||
memset (mp3parse->xing_seek_table_inverse, 0, 256);
|
||||
|
||||
mp3parse->vbri_bitrate = 0;
|
||||
mp3parse->vbri_frames = 0;
|
||||
mp3parse->vbri_total_time = 0;
|
||||
mp3parse->vbri_bytes = 0;
|
||||
|
||||
if (mp3parse->seek_table) {
|
||||
g_list_foreach (mp3parse->seek_table, (GFunc) g_free, NULL);
|
||||
|
@ -573,6 +584,8 @@ gst_mp3parse_emit_frame (GstMPEGAudioParse * mp3parse, guint size)
|
|||
/* Post a bitrate tag if we need to before pushing the buffer */
|
||||
if (mp3parse->xing_bitrate != 0)
|
||||
bitrate = mp3parse->xing_bitrate;
|
||||
else if (mp3parse->vbri_bitrate != 0)
|
||||
bitrate = mp3parse->vbri_bitrate;
|
||||
else
|
||||
bitrate = mp3parse->avg_bitrate;
|
||||
|
||||
|
@ -647,8 +660,9 @@ gst_mp3parse_handle_first_frame (GstMPEGAudioParse * mp3parse)
|
|||
gchar *codec;
|
||||
const guint32 xing_id = 0x58696e67; /* 'Xing' in hex */
|
||||
const guint32 info_id = 0x496e666f; /* 'Info' in hex - found in LAME CBR files */
|
||||
const guint XING_HDR_MIN = 8;
|
||||
gint xing_offset;
|
||||
const guint32 vbri_id = 0x56425249; /* 'VBRI' in hex */
|
||||
|
||||
gint offset;
|
||||
|
||||
guint64 avail;
|
||||
guint32 read_id;
|
||||
|
@ -678,34 +692,34 @@ gst_mp3parse_handle_first_frame (GstMPEGAudioParse * mp3parse)
|
|||
/* Check first frame for Xing info */
|
||||
if (mp3parse->version == 1) { /* MPEG-1 file */
|
||||
if (mp3parse->channels == 1)
|
||||
xing_offset = 0x11;
|
||||
offset = 0x11;
|
||||
else
|
||||
xing_offset = 0x20;
|
||||
offset = 0x20;
|
||||
} else { /* MPEG-2 header */
|
||||
if (mp3parse->channels == 1)
|
||||
xing_offset = 0x09;
|
||||
offset = 0x09;
|
||||
else
|
||||
xing_offset = 0x11;
|
||||
offset = 0x11;
|
||||
}
|
||||
/* Skip the 4 bytes of the MP3 header too */
|
||||
xing_offset += 4;
|
||||
offset += 4;
|
||||
|
||||
/* Check if we have enough data to read the Xing header */
|
||||
avail = gst_adapter_available (mp3parse->adapter);
|
||||
|
||||
if (avail < xing_offset + XING_HDR_MIN)
|
||||
if (avail < offset + 8)
|
||||
return;
|
||||
|
||||
data = gst_adapter_peek (mp3parse->adapter, xing_offset + XING_HDR_MIN);
|
||||
data = gst_adapter_peek (mp3parse->adapter, offset + 8);
|
||||
if (data == NULL)
|
||||
return;
|
||||
/* The header starts at the provided offset */
|
||||
data += xing_offset;
|
||||
data += offset;
|
||||
|
||||
read_id = GST_READ_UINT32_BE (data);
|
||||
if (read_id == xing_id || read_id == info_id) {
|
||||
guint32 xing_flags;
|
||||
guint bytes_needed = xing_offset + XING_HDR_MIN;
|
||||
guint bytes_needed = offset + 8;
|
||||
gint64 total_bytes;
|
||||
GstClockTime total_time;
|
||||
|
||||
|
@ -730,7 +744,7 @@ gst_mp3parse_handle_first_frame (GstMPEGAudioParse * mp3parse)
|
|||
GST_DEBUG_OBJECT (mp3parse, "Reading Xing header");
|
||||
mp3parse->xing_flags = xing_flags;
|
||||
data = gst_adapter_peek (mp3parse->adapter, bytes_needed);
|
||||
data += xing_offset + XING_HDR_MIN;
|
||||
data += offset + 8;
|
||||
|
||||
if (xing_flags & XING_FRAMES_FLAG) {
|
||||
mp3parse->xing_frames = GST_READ_UINT32_BE (data);
|
||||
|
@ -832,10 +846,105 @@ gst_mp3parse_handle_first_frame (GstMPEGAudioParse * mp3parse)
|
|||
mp3parse->xing_vbr_scale = 0;
|
||||
|
||||
GST_DEBUG_OBJECT (mp3parse, "Xing header reported %u frames, time %"
|
||||
G_GUINT64_FORMAT ", vbr scale %u", mp3parse->xing_frames,
|
||||
mp3parse->xing_total_time, mp3parse->xing_vbr_scale);
|
||||
GST_TIME_FORMAT ", %u bytes, vbr scale %u", mp3parse->xing_frames,
|
||||
GST_TIME_ARGS (mp3parse->xing_total_time), mp3parse->xing_bytes,
|
||||
mp3parse->xing_vbr_scale);
|
||||
} else if (read_id == vbri_id) {
|
||||
gint64 total_bytes, total_frames;
|
||||
GstClockTime total_time;
|
||||
|
||||
/* guint16 nseek_points; */
|
||||
|
||||
GST_DEBUG_OBJECT (mp3parse, "Found VBRI header marker 0x%x", vbri_id);
|
||||
if (avail < offset + 26) {
|
||||
GST_DEBUG_OBJECT (mp3parse,
|
||||
"Not enough data to read VBRI header (need %d)", offset + 26);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (mp3parse, "Reading VBRI header");
|
||||
data = gst_adapter_peek (mp3parse->adapter, offset + 26);
|
||||
data += offset + 4;
|
||||
|
||||
g_print ("0x%x %c\n", *(data - 1), *(data - 1));
|
||||
|
||||
if (GST_READ_UINT16_BE (data) != 0x0001) {
|
||||
GST_DEBUG_OBJECT (mp3parse,
|
||||
"Unsupported VBRI version 0x%x", GST_READ_UINT16_BE (data));
|
||||
return;
|
||||
}
|
||||
data += 2;
|
||||
|
||||
/* Skip encoder delay */
|
||||
data += 2;
|
||||
|
||||
/* Skip quality */
|
||||
data += 2;
|
||||
|
||||
total_bytes = GST_READ_UINT32_BE (data);
|
||||
if (total_bytes != 0)
|
||||
mp3parse->vbri_bytes = total_bytes;
|
||||
data += 4;
|
||||
|
||||
total_frames = GST_READ_UINT32_BE (data);
|
||||
if (total_frames != 0) {
|
||||
mp3parse->vbri_frames = total_frames;
|
||||
mp3parse->vbri_total_time = gst_util_uint64_scale (GST_SECOND,
|
||||
(guint64) (mp3parse->vbri_frames) * (mp3parse->spf), mp3parse->rate);
|
||||
}
|
||||
data += 4;
|
||||
|
||||
/* If we know the upstream size and duration, compute the
|
||||
* total bitrate, rounded up to the nearest kbit/sec */
|
||||
if (mp3parse_total_time (mp3parse, &total_time) &&
|
||||
mp3parse_total_bytes (mp3parse, &total_bytes)) {
|
||||
mp3parse->vbri_bitrate = gst_util_uint64_scale (total_bytes,
|
||||
8 * GST_SECOND, total_time);
|
||||
mp3parse->vbri_bitrate += 500;
|
||||
mp3parse->vbri_bitrate -= mp3parse->vbri_bitrate % 1000;
|
||||
}
|
||||
|
||||
/* TODO: Parse seek table and use everywhere.
|
||||
* See http://groups.google.com/group/alt.music.mp3/browse_thread/thread/4036a2ad8f2ed55d/a528fc7afdf353f6?#a528fc7afdf353f6
|
||||
*/
|
||||
#if 0
|
||||
nseek_points = GST_READ_UINT16_BE (data);
|
||||
data += 2;
|
||||
|
||||
if (GST_READ_UINT32_BE (data) != 0x0102) {
|
||||
GST_DEBUG_OBJECT (mp3parse, "Unsupported VBRI seek table");
|
||||
return;
|
||||
} else if (nseek_points > 0) {
|
||||
guint stride;
|
||||
|
||||
data += 4;
|
||||
|
||||
stride = GST_READ_UINT16_BE (data);
|
||||
if (stride == 0) {
|
||||
GST_DEBUG_OBJECT (mp3parse, "Unsupported VBRI seek table");
|
||||
return;
|
||||
}
|
||||
|
||||
if (avail < offset + 26 + nseek_points * 2) {
|
||||
GST_DEBUG_OBJECT (mp3parse,
|
||||
"Not enough data to read VBRI seek table (need %d)",
|
||||
offset + 26 + nseek_points * 2);
|
||||
return;
|
||||
}
|
||||
|
||||
data =
|
||||
gst_adapter_peek (mp3parse->adapter, offset + 26 + nseek_points * 2);
|
||||
data += offset + 26;
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
GST_DEBUG_OBJECT (mp3parse, "VBRI header reported %u frames, time %"
|
||||
GST_TIME_FORMAT ", bytes %u", mp3parse->vbri_frames,
|
||||
GST_TIME_ARGS (mp3parse->vbri_total_time), mp3parse->vbri_bytes);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (mp3parse, "Xing header not found in first frame");
|
||||
GST_DEBUG_OBJECT (mp3parse,
|
||||
"Xing, LAME or VBRI header not found in first frame");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1151,6 +1260,11 @@ mp3parse_total_bytes (GstMPEGAudioParse * mp3parse, gint64 * total)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
if (mp3parse->vbri_bytes != 0) {
|
||||
*total = mp3parse->vbri_bytes;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -1166,6 +1280,11 @@ mp3parse_total_time (GstMPEGAudioParse * mp3parse, GstClockTime * total)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
if (mp3parse->vbri_total_time != 0) {
|
||||
*total = mp3parse->vbri_total_time;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Calculate time from the measured bitrate */
|
||||
if (!mp3parse_total_bytes (mp3parse, &total_bytes))
|
||||
return FALSE;
|
||||
|
|
|
@ -103,6 +103,12 @@ struct _GstMPEGAudioParse {
|
|||
guint32 xing_vbr_scale;
|
||||
guint xing_bitrate;
|
||||
|
||||
/* VBRI info */
|
||||
guint32 vbri_frames;
|
||||
GstClockTime vbri_total_time;
|
||||
guint32 vbri_bytes;
|
||||
guint vbri_bitrate;
|
||||
|
||||
/* Accurate seeking */
|
||||
GList *seek_table;
|
||||
GMutex *pending_accurate_seeks_lock;
|
||||
|
|
Loading…
Reference in a new issue