summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThibaut Mattern <tmattern@users.sourceforge.net>2003-09-28 14:16:00 +0000
committerThibaut Mattern <tmattern@users.sourceforge.net>2003-09-28 14:16:00 +0000
commitdef6a5ad4dc230d936a46f99d4c378f9a8759660 (patch)
treecc73e1750131ca460dcbc16dc286223108d843b6 /src
parente1955ffcb3b67093e0828e515bd43411a7e272cf (diff)
downloadxine-lib-def6a5ad4dc230d936a46f99d4c378f9a8759660.tar.gz
xine-lib-def6a5ad4dc230d936a46f99d4c378f9a8759660.tar.bz2
Revisited mpeg audio demuxer.
- remove duplicated frame parsing code - parse each mpeg audio frame => generate accurate pts, the viz plugin hack to get things in sync is no more needed - parse the full xing header => compute the length (in s) of the stream without "magic" stuff => use the Xing TOC to seek "accurately" in vbr streams. - prepare id3v2 tags parsing I think this big change was needed. Don't hesitate to blame me for bugs. CVS patchset: 5420 CVS date: 2003/09/28 14:16:00
Diffstat (limited to 'src')
-rw-r--r--src/demuxers/demux_mpgaudio.c706
1 files changed, 444 insertions, 262 deletions
diff --git a/src/demuxers/demux_mpgaudio.c b/src/demuxers/demux_mpgaudio.c
index 0826a7c3f..0009df3fa 100644
--- a/src/demuxers/demux_mpgaudio.c
+++ b/src/demuxers/demux_mpgaudio.c
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: demux_mpgaudio.c,v 1.108 2003/09/22 23:16:14 tmattern Exp $
+ * $Id: demux_mpgaudio.c,v 1.109 2003/09/28 14:16:00 tmattern Exp $
*
* demultiplexer for mpeg audio (i.e. mp3) streams
*
+ * mp3 file structure:
+ * [id3v2] [Xing] Frame1 Frame2 Frame3... [id3v1]
+ *
*/
#ifdef HAVE_CONFIG_H
@@ -60,7 +63,39 @@
#define RIFF_TAG FOURCC_TAG('R', 'I', 'F', 'F')
#define AVI_TAG FOURCC_TAG('A', 'V', 'I', ' ')
#define CDXA_TAG FOURCC_TAG('C', 'D', 'X', 'A')
-#define ID3V2_TAG FOURCC_TAG('I', 'D', '3', 0) /* ID3 v2.x.y tags */
+#define ID3V22_TAG FOURCC_TAG('I', 'D', '3', 2) /* ID3 v2.2 tags */
+#define ID3V23_TAG FOURCC_TAG('I', 'D', '3', 3) /* ID3 v2.3 tags */
+#define ID3V24_TAG FOURCC_TAG('I', 'D', '3', 4) /* ID3 v2.4 tags */
+#define XING_TAG FOURCC_TAG('X', 'i', 'n', 'g')
+
+/* Xing header stuff */
+#define XING_FRAMES_FLAG 0x0001
+#define XING_BYTES_FLAG 0x0002
+#define XING_TOC_FLAG 0x0004
+#define XING_VBR_SCALE_FLAG 0x0008
+
+typedef struct {
+ /* header */
+ uint16_t frame_sync;
+ uint8_t mpeg25_bit;
+ uint8_t lsf_bit;
+ uint8_t layer;
+ uint8_t protection_bit;
+ uint8_t bitrate_idx;
+ uint8_t freq_idx;
+ uint8_t padding_bit;
+ uint8_t private_bit;
+ uint8_t channel_mode;
+ uint8_t mode_extension;
+ uint8_t copyright;
+ uint8_t original;
+ uint8_t emphasis;
+
+ uint8_t version_idx;
+ int bitrate;
+ int samplerate;
+ int length; /* in bytes */
+} mpg_audio_frame_t;
typedef struct {
@@ -75,12 +110,24 @@ typedef struct {
int status;
int stream_length;
- long bitrate;
+ int br; /* bitrate */
+ int abr; /* average bitrate */
int64_t last_pts;
int send_newpts;
int buf_flag_seek;
uint32_t blocksize;
+ mpg_audio_frame_t cur_frame;
+ double cur_fpts;
+ int is_vbr;
+
+ /* Xing header */
+ int check_xing;
+ uint32_t xflags;
+ uint32_t xframes;
+ uint32_t xbytes;
+ uint8_t xtoc[100];
+ uint32_t xvbr_scale;
} demux_mpgaudio_t ;
typedef struct {
@@ -106,80 +153,250 @@ const int tabsel_123[2][3][16] = {
{0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,} }
};
-static int frequencies[2][3] = {
+static int frequencies[3][3] = {
{ 44100, 48000, 32000 },
- { 22050, 24000, 16000 }
+ { 22050, 24000, 16000 },
+ { 11025, 12000, 8000 }
};
-static int mpg123_head_check(unsigned long head) {
- if ((head & 0xffe00000) != 0xffe00000)
- return 0;
- if (!((head >> 17) & 3))
- return 0;
- if (((head >> 12) & 0xf) == 0xf)
+static int mpg123_xhead_check(char *buf)
+{
+ return (BE_32(buf) == XING_TAG);
+}
+
+static void check_newpts (demux_mpgaudio_t *this, int64_t pts) {
+
+ int64_t diff;
+
+ diff = pts - this->last_pts;
+
+ if( pts &&
+ (this->send_newpts || (this->last_pts && abs(diff)>WRAP_THRESHOLD) ) ) {
+ if (this->buf_flag_seek) {
+ xine_demux_control_newpts(this->stream, pts, BUF_FLAG_SEEK);
+ this->buf_flag_seek = 0;
+ } else {
+ xine_demux_control_newpts(this->stream, pts, 0);
+ }
+ this->send_newpts = 0;
+ }
+
+ if( pts )
+ this->last_pts = pts;
+}
+
+static int mpg123_parse_frame_header(mpg_audio_frame_t *frame, uint8_t *buf) {
+ uint32_t head;
+
+ head = BE_32(buf);
+
+ lprintf("header: %08X\n", head);
+ frame->frame_sync = head >> 21;
+ if (frame->frame_sync != 0x7ff) {
+ lprintf("invalid frame sync\n");
return 0;
- if (!((head >> 12) & 0xf))
+ }
+
+ 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
+ frame->version_idx = 2; /* MPEG Version 2.5 */
+ } else {
+ if (!frame->lsf_bit)
+ frame->version_idx = 1; /* MPEG Version 2 */
+ else
+ frame->version_idx = 0; /* MPEG Version 1 */
+ }
+
+ frame->layer = 4 - ((head >> 17) & 0x3);
+ if (frame->layer == 4) {
+ lprintf("reserved layer\n");
return 0;
- if (((head >> 10) & 0x3) == 0x3)
+ }
+
+ frame->protection_bit = (head >> 16) & 0x1;
+ frame->bitrate_idx = (head >> 12) & 0xf;
+ if ((frame->bitrate_idx == 0) || (frame->bitrate_idx == 15)) {
+ lprintf("invalid bitrate index\n");
return 0;
- if (((head >> 19) & 1) == 1
- && ((head >> 17) & 3) == 3
- && ((head >> 16) & 1) == 1)
+ }
+
+ frame->freq_idx = (head >> 10) & 0x3;
+ if (frame->freq_idx == 3) {
+ lprintf("invalid frequence index\n");
return 0;
- if ((head & 0xffff0000) == 0xfffe0000)
+ }
+
+ frame->padding_bit = (head >> 9) & 0x1;
+ frame->private_bit = (head >> 8) & 0x1;
+ frame->channel_mode = (head >> 6) & 0x3;
+ frame->mode_extension = (head >> 4) & 0x3;
+ frame->copyright = (head >> 3) & 0x1;
+ frame->original = (head >> 2) & 0x1;
+ frame->emphasis = head & 0x3;
+ if (frame->emphasis == 2) {
+ lprintf("reserved emphasis\n");
return 0;
+ }
+ frame->bitrate = tabsel_123[!frame->lsf_bit][frame->layer - 1][frame->bitrate_idx] * 1000;
+ frame->samplerate = frequencies[frame->version_idx][frame->freq_idx];
+ if (frame->layer == 1) {
+ frame->length = (12 * frame->bitrate / frame->samplerate + frame->padding_bit) * 4;
+ } else {
+ frame->length = 144 * frame->bitrate / frame->samplerate + frame->padding_bit;
+ }
+ lprintf("bitrate: %d bps\n", frame->bitrate);
+ lprintf("samplerate: %d Hz\n", frame->samplerate);
+ lprintf("length: %d bytes, %d ms\n", frame->length, (1000 * frame->length) / (frame->bitrate / 8));
return 1;
}
-static int mpg123_xhead_check(unsigned char *buf)
-{
- if (buf[3] != 'X')
- return 0;
- if (buf[2] != 'i')
- return 0;
- if (buf[1] != 'n')
- return 0;
- if (buf[0] != 'g')
- return 0;
+static int mpg123_parse_xing_header(demux_mpgaudio_t *this, uint8_t *buf, int bufsize) {
- return 1;
+ int i;
+ uint8_t *ptr = buf;
+ double frame_duration;
+
+ /* offset of the Xing header */
+ if( this->cur_frame.mpeg25_bit ) {
+ /* mpeg1 */
+ if( this->cur_frame.channel_mode != 3 )
+ ptr += (32 + 4);
+ else
+ ptr += (17 + 4);
+ } else {
+ /* mpeg2 */
+ if( this->cur_frame.channel_mode != 3 )
+ ptr += (17 + 4);
+ else
+ ptr += (9 + 4);
+ }
+
+ if (ptr >= (buf + bufsize)) return 0;
+ lprintf("checking %08X\n", *ptr);
+ if (mpg123_xhead_check(ptr)) {
+ lprintf("Xing header found\n");
+
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+
+ this->xflags = BE_32(ptr);
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+
+ if (this->xflags & XING_FRAMES_FLAG) {
+ this->xframes = BE_32(ptr);
+ lprintf("xframes: %d\n", this->xframes);
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+ }
+ if (this->xflags & XING_BYTES_FLAG) {
+ this->xbytes = BE_32(ptr);
+ lprintf("xbytes: %d\n", this->xbytes);
+ ptr += 4; if (ptr >= (buf + bufsize)) return 0;
+ }
+ if (this->xflags & XING_TOC_FLAG) {
+ lprintf("toc found\n");
+ for (i = 0; i < 100; i++) {
+ this->xtoc[i] = *(ptr + i);
+#ifdef LOG
+ printf("%d ", this->xtoc[i]);
+#endif
+ }
+#ifdef LOG
+ printf("\n");
+#endif
+ }
+ ptr += 100; if (ptr >= (buf + bufsize)) return 0;
+ this->xvbr_scale = -1;
+ if (this->xflags & XING_VBR_SCALE_FLAG) {
+ this->xvbr_scale = BE_32(ptr);
+ lprintf("xvbr_scale: %d\n", this->xvbr_scale);
+ }
+
+ /* 1 kbit = 1000 bits ! (and not 1024 bits) */
+ if (this->xflags & (XING_FRAMES_FLAG | XING_BYTES_FLAG)) {
+ if (this->cur_frame.layer == 1) {
+ frame_duration = 384.0 / (double)this->cur_frame.samplerate;
+ } else {
+ frame_duration = 1152.0 / (double)this->cur_frame.samplerate;
+ }
+ this->abr = ((double)this->xbytes * 8.0) / ((double)this->xframes * frame_duration);
+ this->stream_length = (double)this->xframes * frame_duration;
+ this->is_vbr = 1;
+ lprintf("abr: %d bps\n", this->abr);
+ lprintf("stream_length: %d s, %d min %d s\n", this->stream_length,
+ this->stream_length / 60, this->stream_length % 60);
+ } else {
+ /* it's a stupid Xing header */
+ this->is_vbr = 0;
+ }
+ return 1;
+ } else {
+ lprintf("Xing header not found\n");
+ return 0;
+ }
}
+static int mpg123_parse_frame_payload(demux_mpgaudio_t *this,
+ uint8_t *frame_header,
+ int decoder_flags) {
+ buf_element_t *buf;
+ off_t frame_pos, len;
+ uint64_t pts = 0;
-/* Return length of an MP3 frame using potential 32-bit header value. See
- * "http://www.dv.co.yu/mpgscript/mpeghdr.htm" for details on the header
- * format.
- *
- * NOTE: As an optimization and because they are rare, this returns 0 for
- * version 2.5 or free format MP3s.
- */
-static size_t get_mp3_frame_length (unsigned long mp3_header)
-{
- int ver = 4 - ((mp3_header >> 19) & 3u);
- int br = (mp3_header >> 12) & 0xfu;
- int srf = (mp3_header >> 10) & 3u;
-
- /* are frame sync and layer 3 bits set? */
- if (((mp3_header & 0xffe20000ul) == 0xffe20000ul)
- /* good version? */
- && ((ver == 1) || (ver == 2))
- /* good bitrate index (not free or invalid)? */
- && (br > 0) && (br < 15)
- /* good sampling rate frequency index? */
- && (srf != 3)
- /* not using reserved emphasis value? */
- && ((mp3_header & 3u) != 2)) {
-
- /* then this is most likely the beginning of a valid frame */
- size_t length = (size_t) tabsel_123[ver - 1][2][br] * 144000;
- length /= frequencies[ver - 1][srf];
- return length += ((mp3_header >> 9) & 1u) - 4;
+ frame_pos = this->input->get_current_pos(this->input) - 4;
+ lprintf("frame_pos = %lld\n", frame_pos);
+
+ buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo);
+
+ /* the decoder needs the frame header */
+ memcpy(buf->mem, frame_header, 4);
+
+ len = this->input->read(this->input, buf->mem + 4, this->cur_frame.length - 4);
+ if (len != (this->cur_frame.length - 4)) {
+ buf->free_buffer(buf);
+ return 0;
}
- return 0;
+
+ /*
+ * compute stream length (in s)
+ * use the Xing header if there is one (VBR)
+ * otherwise use CBR formula
+ */
+ if (this->check_xing) {
+ mpg123_parse_xing_header(this, buf->mem, len + 4);
+ if (!this->is_vbr) {
+ this->stream_length = this->input->get_length(this->input) / (this->br / 8);
+ }
+ this->check_xing = 0;
+ }
+
+ if (this->cur_frame.layer == 1) {
+ this->cur_fpts += 90000.0 * 384.0 / (double)this->cur_frame.samplerate;
+ } else {
+ this->cur_fpts += 90000.0 * 1152.0 / (double)this->cur_frame.samplerate;
+ }
+ pts = (int64_t)this->cur_fpts;
+ check_newpts(this, pts);
+
+ buf->extra_info->input_pos = frame_pos;
+ buf->extra_info->input_time = pts / 90;
+ buf->pts = pts;
+ buf->size = len + 4;
+ buf->content = buf->mem;
+ buf->type = BUF_AUDIO_MPEG;
+ buf->decoder_info[0] = 1;
+ buf->decoder_flags = decoder_flags;
+
+ this->audio_fifo->put(this->audio_fifo, buf);
+ return 1;
}
+
static unsigned char * demux_mpgaudio_read_buffer_header (input_plugin_t *input)
{
int count;
@@ -211,54 +428,43 @@ static unsigned char * demux_mpgaudio_read_buffer_header (input_plugin_t *input)
/* Scan through the first SNIFF_BUFFER_LENGTH bytes of the
* buffer to find a potential 32-bit MP3 frame header. */
-static int _sniff_buffer_looks_like_mp3 (input_plugin_t *input)
+static int sniff_buffer_looks_like_mp3 (input_plugin_t *input)
{
- unsigned long mp3_header;
int offset;
unsigned char *buf;
+ mpg_audio_frame_t frame;
buf = demux_mpgaudio_read_buffer_header (input);
if (buf == NULL)
return 0;
- mp3_header = 0;
for (offset = 0; offset < SNIFF_BUFFER_LENGTH; offset++) {
size_t length;
- mp3_header <<= 8;
- mp3_header |= buf[offset];
- mp3_header &= 0xfffffffful;
-
- length = get_mp3_frame_length (mp3_header);
+ if (mpg123_parse_frame_header(&frame, buf + offset)) {
+ length = frame.length;
- if (length != 0) {
/* Since one frame is available, is there another frame
* just to be sure this is more likely to be a real MP3
* buffer? */
- offset += 1 + length;
-
- if(((mp3_header >> 16) & 1) == 1)
- offset -= 2;
+ offset += length;
if (offset + 4 > SNIFF_BUFFER_LENGTH)
{
free (buf);
- return 0;
+ return 0;
}
- mp3_header = BE_32(&buf[offset]);
- length = get_mp3_frame_length (mp3_header);
-
- if (length != 0) {
+ if (mpg123_parse_frame_header(&frame, buf + offset)) {
free (buf);
- return 1;
+ lprintf("mpeg audio frame detected\n");
+ return 1;
}
break;
}
}
free (buf);
-
return 0;
}
@@ -310,174 +516,76 @@ static void read_id3_tags (demux_mpgaudio_t *this) {
chomp (tag.comment);
this->stream->meta_info [XINE_META_INFO_TITLE]
- = strdup (tag.title);
+ = strdup (tag.title);
this->stream->meta_info [XINE_META_INFO_ARTIST]
- = strdup (tag.artist);
+ = strdup (tag.artist);
this->stream->meta_info [XINE_META_INFO_ALBUM]
- = strdup (tag.album);
+ = strdup (tag.album);
this->stream->meta_info [XINE_META_INFO_COMMENT]
- = strdup (tag.comment);
+ = strdup (tag.comment);
}
}
}
-static void mpg123_decode_header(demux_mpgaudio_t *this, unsigned long newhead) {
- int lsf, mpeg25;
- int lay, bitrate_index;
- char *ver;
-
- /*
- * lsf==0 && mpeg25==0 : MPEG Version 1 (ISO/IEC 11172-3)
- * lsf==1 && mpeg25==0 : MPEG Version 2 (ISO/IEC 13818-3)
- * lsf==1 && mpeg25==1 : MPEG Version 2.5 (later extension of MPEG 2)
- */
-
- mpeg25 = !((newhead >> 20) & 1); /* mpeg 2.5 ext */
- lsf = !((newhead >> 19) & 1); /* lsf ext */
-
- if (mpeg25)
- ver = "2.5";
- else
- ver = (lsf) ? "2" : "1";
-
- /* Layer I, II, III */
- lay = 4 - ((newhead >> 17) & 3);
- bitrate_index = ((newhead >> 12) & 0xf);
-
- this->bitrate = tabsel_123[lsf][lay - 1][bitrate_index] * 1000;
+static int mpg123_read_frame_header(demux_mpgaudio_t *this, uint8_t *header_buf, int bytes) {
+ off_t len;
+ int i;
- if( !this->bitrate ) /* bitrate can't be zero, default to 128 */
- this->bitrate = 128000;
-
- this->stream->stream_info[XINE_STREAM_INFO_BITRATE] = this->bitrate;
- lprintf("mpeg %s audio layer %d\n", ver, lay);
- lprintf("bitrate: %ld\n", this->bitrate);
- this->stream_length = (int)(this->input->get_length(this->input) / (this->bitrate / 8));
- lprintf("stream_length: %d s\n", this->stream_length);
-}
-
-static void check_newpts( demux_mpgaudio_t *this, int64_t pts ) {
-
- int64_t diff;
-
- diff = pts - this->last_pts;
-
- if( pts &&
- (this->send_newpts || (this->last_pts && abs(diff)>WRAP_THRESHOLD) ) ) {
- if (this->buf_flag_seek) {
- xine_demux_control_newpts(this->stream, pts, BUF_FLAG_SEEK);
- this->buf_flag_seek = 0;
- } else {
- xine_demux_control_newpts(this->stream, pts, 0);
- }
- this->send_newpts = 0;
+ for (i = 0; i < (4 - bytes); i++) {
+ header_buf[i] = header_buf[i + bytes];
}
- if( pts )
- this->last_pts = pts;
+ len = this->input->read(this->input, header_buf + 4 - bytes, bytes);
+ if (len != bytes) {
+ return 0;
+ }
+ return 1;
}
static int demux_mpgaudio_next (demux_mpgaudio_t *this, int decoder_flags) {
-
- buf_element_t *buf;
- uint32_t blocksize;
- uint32_t head;
- off_t buffer_pos;
- off_t done;
- uint64_t pts = 0;
-
- buffer_pos = this->input->get_current_pos(this->input);
+ uint8_t header_buf[4];
+ int bytes = 4;
if (!this->audio_fifo)
return 0;
- buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo);
+ for (;;) {
- blocksize = (this->blocksize ? this->blocksize : buf->max_size);
- done = this->input->read(this->input, buf->mem, blocksize);
+ if (mpg123_read_frame_header(this, header_buf, bytes)) {
- if (done <= 0) {
- buf->free_buffer(buf);
- return 0;
- }
-
- if (this->bitrate == 0) {
- int i, ver, srindex, brindex, xbytes, xframes;
-
- for( i = 0; i < done-4; i++ ) {
- head = (buf->mem[i+0] << 24) + (buf->mem[i+1] << 16) +
- (buf->mem[i+2] << 8) + buf->mem[i+3];
-
- if (mpg123_head_check(head)) {
- mpg123_decode_header(this,head);
- break;
- }
- }
- /* Now check for the Xing header to get the correct bitrate */
- ver = (buf->mem[i+1] & 0x08) >> 3;
- brindex = (buf->mem[i+2] & 0xf0) >> 4;
- srindex = (buf->mem[i+2] & 0x0c) >> 2;
+ if (mpg123_parse_frame_header(&this->cur_frame, header_buf)) {
- for( i = 0; i < buf->size-16; i++ ) {
- head = (buf->mem[i+0] << 24) + (buf->mem[i+1] << 16) +
- (buf->mem[i+2] << 8) + buf->mem[i+3];
+ if (!this->br) {
+ this->br = this->cur_frame.bitrate;
+ }
+ return mpg123_parse_frame_payload(this, header_buf, decoder_flags);
- if (mpg123_xhead_check((unsigned char *)&head)) {
- long long total_bytes, magic1, magic2;
+ } else if ((BE_32(header_buf)) == ID3V22_TAG) {
+ lprintf("ID3V2.2 tag\n");
+ /* TODO: add parsing here */
+ bytes = 1;
- xframes = BE_32(buf->mem+i+8);
- xbytes = BE_32(buf->mem+i+12);
+ } else if ((BE_32(header_buf)) == ID3V23_TAG) {
+ lprintf("ID3V2.3 tag\n");
+ /* TODO: add parsing here */
+ bytes = 1;
- if (xframes <= 0) {
- break;
- }
+ } else if ((BE_32(header_buf)) == ID3V24_TAG) {
+ lprintf("ID3V2.4 tag\n");
+ /* TODO: add parsing here */
+ bytes = 1;
- total_bytes = (long long) frequencies[!ver][srindex] * (long long) xbytes;
- magic1 = total_bytes / (long long) (576 + ver * 576);
- magic2 = magic1 / (long long) xframes;
-
- /* 1 kbit = 1000 bits ! (and not 1024 bits) */
- this->bitrate = (int) ((long long) magic2 / (long long) 125) * 1000;
-
- this->stream->stream_info[XINE_STREAM_INFO_BITRATE] = this->bitrate;
-
- this->stream_length = (int)(this->input->get_length(this->input) / (this->bitrate / 8));
- break;
+ } else {
+ /* skip */
+ bytes = 1;
}
- }
- }
- buf->pts = 0;
- if (this->bitrate) {
- pts = (90000 * buffer_pos) / (this->bitrate / 8);
- check_newpts(this, pts);
- }
-#if 0
- buf->pts = pts;
-#endif
-
- buf->extra_info->input_pos = this->input->get_current_pos(this->input);
- {
- int len = this->input->get_length(this->input);
- if (len>0)
- buf->extra_info->input_time = (int)((int64_t)buf->extra_info->input_pos
- * this->stream_length * 1000 / len);
- else
- buf->extra_info->input_time = pts / 90;
+ } else {
+ lprintf("read error\n");
+ return 0;
+ }
}
-#if 0
- buf->pts = pts;
-#endif
- buf->size = done;
- buf->content = buf->mem;
- buf->type = BUF_AUDIO_MPEG;
- buf->decoder_info[0] = 1;
- buf->decoder_flags = decoder_flags;
-
- this->audio_fifo->put(this->audio_fifo, buf);
-
- return 1;
}
static int demux_mpgaudio_send_chunk (demux_plugin_t *this_gen) {
@@ -496,9 +604,8 @@ static int demux_mpgaudio_get_status (demux_plugin_t *this_gen) {
return this->status;
}
-static uint32_t demux_mpgaudio_read_head(input_plugin_t *input, uint8_t *buf) {
+static int demux_mpgaudio_read_head(input_plugin_t *input, uint8_t *buf) {
- uint32_t head=0;
int bs = 0;
int i, optional;
@@ -514,8 +621,7 @@ static uint32_t demux_mpgaudio_read_head(input_plugin_t *input, uint8_t *buf) {
if(!bs)
bs = MAX_PREVIEW_SIZE;
- if(input->read(input, buf, bs))
- head = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+ input->read(input, buf, bs);
lprintf("stream is seekable\n");
@@ -530,52 +636,39 @@ static uint32_t demux_mpgaudio_read_head(input_plugin_t *input, uint8_t *buf) {
buf[0], buf[1], buf[2], buf[3]);
for(i = 0; i < (optional - 4); i++) {
- head = (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + buf[i + 3];
- if(head == RIFF_TAG)
- return head;
+ if (BE_32(buf + i) == RIFF_TAG)
+ return 1;
}
-
- head = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
-
} else {
lprintf("not seekable, no preview\n");
return 0;
}
-
- return head;
+ return 1;
}
static void demux_mpgaudio_send_headers (demux_plugin_t *this_gen) {
demux_mpgaudio_t *this = (demux_mpgaudio_t *) this_gen;
- uint8_t buf[MAX_PREVIEW_SIZE];
int i;
this->stream_length = 0;
- this->bitrate = 0;
this->last_pts = 0;
this->status = DEMUX_OK;
+ this->check_xing = 1;
this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0;
this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
if ((this->input->get_capabilities(this->input) & INPUT_CAP_SEEKABLE) != 0) {
- uint32_t head;
off_t pos;
- head = demux_mpgaudio_read_head(this->input, buf);
- if (mpg123_head_check(head))
- mpg123_decode_header(this, head);
-
/* check ID3 v1 at the end of the stream */
pos = this->input->get_length(this->input) - 128;
this->input->seek (this->input, pos, SEEK_SET);
read_id3_tags (this);
}
- this->blocksize = this->input->get_blocksize(this->input);
-
/*
* send preview buffers
*/
@@ -589,21 +682,109 @@ static void demux_mpgaudio_send_headers (demux_plugin_t *this_gen) {
break;
}
}
+
+ if (this->is_vbr)
+ this->stream->stream_info[XINE_STREAM_INFO_BITRATE] = this->abr;
+ else
+ this->stream->stream_info[XINE_STREAM_INFO_BITRATE] = this->br;
+
+ if (this->cur_frame.layer == 1)
+ this->stream->stream_info[XINE_STREAM_INFO_FRAME_DURATION] =
+ 384000 / this->cur_frame.samplerate;
+ else
+ this->stream->stream_info[XINE_STREAM_INFO_FRAME_DURATION] =
+ 1152000 / this->cur_frame.samplerate;
+
this->status = DEMUX_OK;
}
+/* interpolate in Xing TOC to get file seek point in bytes */
+static off_t xing_get_seek_point(demux_mpgaudio_t *this, int time)
+{
+ off_t seekpoint;
+ int a;
+ float fa, fb, fx;
+ float percent;
+
+ percent = ((float)time / 10.0f)/ (float)this->stream_length;
+ if (percent < 0.0f) percent = 0.0f;
+ if (percent > 100.0f) percent = 100.0f;
+
+ a = (int)percent;
+ if (a > 99) a = 99;
+ fa = this->xtoc[a];
+ if (a < 99) {
+ fb = this->xtoc[a + 1];
+ } else {
+ fb = 256.0f;
+ }
+
+ fx = fa + (fb - fa) * (percent - a);
+ seekpoint = (off_t)((1.0f / 256.0f) * fx * this->xbytes);
+
+ return seekpoint;
+}
+
+/* interpolate in Xing TOC to get file seek point in ms */
+static int xing_get_seek_time(demux_mpgaudio_t *this, off_t pos)
+{
+ int seektime;
+ int a, b;
+ float fb, fx;
+ float percent;
+
+ fx = 256.0f * (float)pos / (float)this->xbytes;
+ if (fx < 0.0f) fx = 0.0f;
+ if (fx > 256.0f) fx = 256.0f;
+
+ for (b = 0; b < 100; b++) {
+ fb = this->xtoc[b];
+ if (fb > fx)
+ break;
+ }
+
+ if (b > 0) {
+ a = b - 1;
+ } else {
+ a = 0;
+ }
+
+ percent = a + (fx - this->xtoc[a]);
+ seektime = 10.0f * percent * this->stream_length;
+
+ return seektime;
+}
+
static int demux_mpgaudio_seek (demux_plugin_t *this_gen,
- off_t start_pos, int start_time) {
+ off_t start_pos, int start_time) {
demux_mpgaudio_t *this = (demux_mpgaudio_t *) this_gen;
- start_time /= 1000;
if ((this->input->get_capabilities(this->input) & INPUT_CAP_SEEKABLE) != 0) {
- if (!start_pos && start_time && this->stream_length > 0)
- start_pos = start_time * this->input->get_length(this->input) /
- this->stream_length;
-
+ if (!start_pos && start_time && this->stream_length > 0) {
+ if (this->is_vbr && (this->xflags & (XING_TOC_FLAG | XING_BYTES_FLAG))) {
+ /* vbr */
+ start_pos = xing_get_seek_point(this, start_time);
+ lprintf("time seek: vbr\n");
+ } else {
+ /* cbr */
+ start_pos = start_time * this->input->get_length(this->input) /
+ this->stream_length;
+ lprintf("time seek: cbr\n");
+ }
+ } else {
+ if (this->is_vbr && (this->xflags & (XING_TOC_FLAG | XING_BYTES_FLAG))) {
+ /* vbr */
+ start_time = xing_get_seek_time(this, start_pos);
+ lprintf("pos seek: vbr\n");
+ } else {
+ /* cbr */
+ start_time = (1000 * start_pos * this->stream_length) / this->input->get_length(this->input);
+ lprintf("pos seek: cbr\n");
+ }
+ }
+ this->cur_fpts = 90 * start_time;
this->input->seek (this->input, start_pos, SEEK_SET);
}
@@ -612,8 +793,7 @@ static int demux_mpgaudio_seek (demux_plugin_t *this_gen,
if( !this->stream->demux_thread_running ) {
this->buf_flag_seek = 0;
- }
- else {
+ } else {
this->buf_flag_seek = 1;
xine_demux_flush_engine(this->stream);
}
@@ -645,10 +825,9 @@ static int demux_mpgaudio_get_optional_data(demux_plugin_t *this_gen,
}
static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
- input_plugin_t *input_gen) {
+ input_plugin_t *input) {
demux_mpgaudio_t *this;
- input_plugin_t *input = (input_plugin_t *) input_gen;
uint8_t buf[MAX_PREVIEW_SIZE];
uint8_t *riff_check;
int i;
@@ -661,14 +840,16 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
case METHOD_BY_CONTENT: {
uint32_t head;
- head = demux_mpgaudio_read_head (input, buf);
+ if (!demux_mpgaudio_read_head(input, buf))
+ return NULL;
- lprintf("head is %x\n", head);
+ head = BE_32(buf);
+ lprintf("head is %8X\n", head);
if (head == RIFF_TAG) {
int ok;
- lprintf("**** found RIFF tag\n");
+ lprintf("found RIFF tag\n");
/* skip the length */
ptr = buf + 8;
@@ -680,7 +861,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
* marker */
if ((BE_32(riff_check) == AVI_TAG) ||
(BE_32(riff_check) == CDXA_TAG)) {
- lprintf("**** found AVI or CDXA tag\n");
+ lprintf("found AVI or CDXA tag\n");
return NULL;
}
@@ -706,20 +887,21 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
for (i = 0; i < RIFF_CHECK_BYTES - 4; i++) {
head = BE_32(riff_check + i);
- lprintf("**** mpg123: checking %08X\n", head);
+ lprintf("checking %08X\n", head);
- if (mpg123_head_check(head)
- || _sniff_buffer_looks_like_mp3(input))
- ok = 1;
+ if (sniff_buffer_looks_like_mp3(input))
+ ok = 1;
}
if (!ok)
- return NULL;
-
- } else if ((head & 0xFFFFFF00) == ID3V2_TAG) {
- lprintf("id3v2 tag detected (but not parsed)\n");
- } else if (!mpg123_head_check(head) &&
- !_sniff_buffer_looks_like_mp3 (input)) {
+ return NULL;
+ } else if (head == ID3V22_TAG) {
+ lprintf("id3v2.2 tag detected (but not parsed)\n");
+ } else if (head == ID3V23_TAG) {
+ lprintf("id3v2.3 tag detected (but not parsed)\n");
+ } else if (head == ID3V24_TAG) {
+ lprintf("id3v2.4 tag detected (but not parsed)\n");
+ } else if (!sniff_buffer_looks_like_mp3 (input)) {
lprintf ("head_check failed\n");
return NULL;
}