From 26fae61845e0b058e734dacbc6c5a6dee0252440 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Fri, 4 Jan 2008 23:36:14 +0100 Subject: Initial parsing of Xing header LAME extension. --- src/demuxers/demux_mpgaudio.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/demuxers/demux_mpgaudio.c b/src/demuxers/demux_mpgaudio.c index 8e716f095..61c0d4cb5 100644 --- a/src/demuxers/demux_mpgaudio.c +++ b/src/demuxers/demux_mpgaudio.c @@ -65,11 +65,13 @@ /* Xing header stuff */ #define XING_TAG FOURCC_TAG('X', 'i', 'n', 'g') #define INFO_TAG FOURCC_TAG('I', 'n', 'f', 'o') +#define LAME_TAG FOURCC_TAG('L', 'A', 'M', 'E') #define XING_FRAMES_FLAG 0x0001 #define XING_BYTES_FLAG 0x0002 #define XING_TOC_FLAG 0x0004 #define XING_VBR_SCALE_FLAG 0x0008 #define XING_TOC_LENGTH 100 +#define LAME_HEADER_LENGTH 0xC0 /* Xing header stuff */ #define VBRI_TAG FOURCC_TAG('V', 'B', 'R', 'I') @@ -96,6 +98,10 @@ typedef struct { uint32_t stream_size; uint8_t toc[XING_TOC_LENGTH]; uint32_t vbr_scale; + + /* Lame extension */ + uint16_t start_delay; + uint16_t end_delay; } xing_header_t; /* Vbri Vbr Header struct */ @@ -380,9 +386,21 @@ static xing_header_t* parse_xing_header(mpg_audio_frame_t *frame, xing->vbr_scale = -1; if (xing->flags & XING_VBR_SCALE_FLAG) { if (ptr >= (buf + bufsize - 4)) goto exit_error; - xing->vbr_scale = _X_BE_32(ptr); + xing->vbr_scale = _X_BE_32(ptr); ptr += 4; lprintf("vbr_scale: %d\n", xing->vbr_scale); } + + /* LAME extension */ + /* see http://gabriel.mp3-tech.org/mp3infotag.html */ + ptr -= 0x9C; /* move offset to match LAME header specs */ + if (ptr + LAME_HEADER_LENGTH >= (buf + bufsize - 4)) goto exit_error; + if (_X_BE_32(&ptr[0x9C]) == LAME_TAG) { + lprintf("Lame header found\n"); + xing->start_delay = (ptr[0xb1] << 4) | (ptr[0xb2] >> 4); + xing->end_delay = ((ptr[0xb2] & 0x0f) << 4) | ptr[0xb3]; + lprintf("start delay : %d samples\n", xing->start_delay); + lprintf("end delay : %d samples\n", xing->end_delay); + } } else { lprintf("Xing header not found\n"); } -- cgit v1.2.3 From 26a5fb652acd73958d3933aa5fb330cfaf12cc70 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Fri, 4 Jan 2008 23:38:46 +0100 Subject: Cosmetics : replace tabs by spaces. --- src/demuxers/demux_mpgaudio.c | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/demuxers/demux_mpgaudio.c b/src/demuxers/demux_mpgaudio.c index 61c0d4cb5..f4fb8fefa 100644 --- a/src/demuxers/demux_mpgaudio.c +++ b/src/demuxers/demux_mpgaudio.c @@ -631,31 +631,31 @@ static int demux_mpgaudio_next (demux_mpgaudio_t *this, int decoder_flags, int s if (parse_frame_header(&this->cur_frame, header_buf)) { - /* send header buffer */ - if ( send_header ) { - buf_element_t *buf; - - buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo); - - buf->type = BUF_AUDIO_MPEG; - buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END; - - buf->decoder_info[0] = 0; - buf->decoder_info[1] = this->cur_frame.freq; - buf->decoder_info[2] = 0; /* bits_per_sample */ - - /* Only for channel_mode == 3 (mono) there is one channel, for any other case, there are 2 */ - buf->decoder_info[3] = ( this->cur_frame.channel_mode == 3 ) ? 1 : 2; - - buf->size = 0; /* No extra header data */ - - this->audio_fifo->put(this->audio_fifo, buf); - } - + /* send header buffer */ + if ( send_header ) { + buf_element_t *buf; + + buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo); + + buf->type = BUF_AUDIO_MPEG; + buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END; + + buf->decoder_info[0] = 0; + buf->decoder_info[1] = this->cur_frame.freq; + buf->decoder_info[2] = 0; /* bits_per_sample */ + + /* Only for channel_mode == 3 (mono) there is one channel, for any other case, there are 2 */ + buf->decoder_info[3] = ( this->cur_frame.channel_mode == 3 ) ? 1 : 2; + + buf->size = 0; /* No extra header data */ + + this->audio_fifo->put(this->audio_fifo, buf); + } + return parse_frame_payload(this, header_buf, decoder_flags); } else if ( id3v2_istag(header_buf) ) { - if (!id3v2_parse_tag(this->input, this->stream, header_buf)) { + if (!id3v2_parse_tag(this->input, this->stream, header_buf)) { xprintf(this->stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": ID3V2 tag parsing error\n"); bytes = 1; /* resync */ @@ -1015,7 +1015,7 @@ static uint32_t demux_mpgaudio_get_capabilities(demux_plugin_t *this_gen) { } static int demux_mpgaudio_get_optional_data(demux_plugin_t *this_gen, - void *data, int data_type) { + void *data, int data_type) { return DEMUX_OPTIONAL_UNSUPPORTED; } -- cgit v1.2.3 From 5d789eac0040b27fd8069be3cdc574a8477f0d51 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Wed, 9 Jan 2008 23:43:29 +0100 Subject: Fixed gapless playback. --- src/demuxers/demux_wav.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/demuxers/demux_wav.c b/src/demuxers/demux_wav.c index 9b46336fe..33baaf973 100644 --- a/src/demuxers/demux_wav.c +++ b/src/demuxers/demux_wav.c @@ -64,6 +64,7 @@ typedef struct { off_t data_start; off_t data_size; + int send_newpts; int seek_flag; /* this is set when a seek just occurred */ } demux_wav_t; @@ -160,9 +161,9 @@ static int demux_wav_send_chunk(demux_plugin_t *this_gen) { current_pts *= 90000; current_pts /= this->wave->nAvgBytesPerSec; - if (this->seek_flag) { - _x_demux_control_newpts(this->stream, current_pts, BUF_FLAG_SEEK); - this->seek_flag = 0; + if (this->send_newpts) { + _x_demux_control_newpts(this->stream, current_pts, this->seek_flag); + this->send_newpts = this->seek_flag = 0; } while (remaining_sample_bytes) { @@ -258,10 +259,13 @@ static int demux_wav_seek (demux_plugin_t *this_gen, start_pos = (off_t) ( (double) start_pos / 65535 * this->data_size ); - this->seek_flag = 1; this->status = DEMUX_OK; - _x_demux_flush_engine (this->stream); + this->send_newpts = 1; + if (playing) { + this->seek_flag = 1; + _x_demux_flush_engine (this->stream); + } /* if input is non-seekable, do not proceed with the rest of this * seek function */ if (!INPUT_IS_SEEKABLE(this->input)) -- cgit v1.2.3 From fae005057d84a7dec9828568f63fb3e3777d3546 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Wed, 9 Jan 2008 23:50:53 +0100 Subject: Added padding delay to the first and the last frame. Output 8 zero-bytes at the end of the stream to flush the decoder. --- src/demuxers/demux_mpgaudio.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/demuxers/demux_mpgaudio.c b/src/demuxers/demux_mpgaudio.c index f4fb8fefa..17916f837 100644 --- a/src/demuxers/demux_mpgaudio.c +++ b/src/demuxers/demux_mpgaudio.c @@ -555,7 +555,22 @@ static int parse_frame_payload(demux_mpgaudio_t *this, buf->content = buf->mem; buf->type = BUF_AUDIO_MPEG; buf->decoder_info[0] = 1; - buf->decoder_flags = decoder_flags|BUF_FLAG_FRAME_END; + buf->decoder_flags = decoder_flags | BUF_FLAG_FRAME_END; + + /* send encoder padding */ + if (this->xing_header) { + if (frame_pos == this->mpg_frame_start) { + lprintf("sending a start padding of %d samples.\n", this->xing_header->start_delay); + buf->decoder_flags = buf->decoder_flags | BUF_FLAG_AUDIO_PADDING; + buf->decoder_info[1] = this->xing_header->start_delay; + buf->decoder_info[2] = 0; + } else if ((frame_pos + this->cur_frame.size) == this->mpg_frame_end) { + lprintf("sending a end padding of %d samples.\n", this->xing_header->end_delay); + buf->decoder_flags = buf->decoder_flags | BUF_FLAG_AUDIO_PADDING; + buf->decoder_info[1] = 0; + buf->decoder_info[2] = this->xing_header->end_delay; + } + } this->audio_fifo->put(this->audio_fifo, buf); lprintf("send buffer: pts=%"PRId64"\n", pts); @@ -678,9 +693,18 @@ static int demux_mpgaudio_send_chunk (demux_plugin_t *this_gen) { demux_mpgaudio_t *this = (demux_mpgaudio_t *) this_gen; - if (!demux_mpgaudio_next (this, 0, 0)) - this->status = DEMUX_FINISHED; + if (!demux_mpgaudio_next (this, 0, 0)) { + /* Hack: send 8 zero bytes to flush the libmad decoder */ + buf_element_t *buf; + buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo); + buf->type = BUF_AUDIO_MPEG; + buf->decoder_flags = BUF_FLAG_FRAME_END; + buf->size = 8; + memset(buf->content, 0, buf->size); + this->audio_fifo->put(this->audio_fifo, buf); + this->status = DEMUX_FINISHED; + } return this->status; } -- cgit v1.2.3 From 9ba5fcd6a1b4330a443239c0ef4fb5ac21009e60 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Thu, 10 Jan 2008 00:01:27 +0100 Subject: Fixed buggy discontinuity handling when playing short streams and using the gapless switch. The current time should not be used here. --- src/xine-engine/metronom.c | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/src/xine-engine/metronom.c b/src/xine-engine/metronom.c index 5d3e0a12d..5bf6495a2 100644 --- a/src/xine-engine/metronom.c +++ b/src/xine-engine/metronom.c @@ -305,6 +305,8 @@ static void metronom_handle_discontinuity (metronom_t *this, int type, /* video_vpts and audio_vpts adjustements */ cur_time = this->xine->clock->get_current_time(this->xine->clock); + xprintf(this->xine, XINE_VERBOSITY_DEBUG, + "current time : %" PRId64 "\n", cur_time); switch (type) { case DISC_STREAMSTART: @@ -321,33 +323,12 @@ static void metronom_handle_discontinuity (metronom_t *this, int type, case DISC_ABSOLUTE: case DISC_RELATIVE: - if (this->video_vpts < cur_time) { - /* still frame */ - if (this->audio_vpts > cur_time) { - /* still frame with audio */ - this->video_vpts = this->audio_vpts; - xprintf(this->xine, XINE_VERBOSITY_DEBUG, "video vpts adjusted to audio vpts %" PRId64 "\n", this->video_vpts); - } else { - /* still frame, no audio */ - this->video_vpts = this->prebuffer + cur_time; - this->audio_vpts = this->video_vpts; - this->audio_vpts_rmndr = 0; - this->force_video_jump = 1; - this->force_audio_jump = 1; - this->video_drift = 0; - xprintf(this->xine, XINE_VERBOSITY_DEBUG, "vpts adjusted with prebuffer to %" PRId64 "\n", - this->video_vpts); - } + if (this->video_vpts < this->audio_vpts) { + this->video_vpts = this->audio_vpts; + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "video vpts adjusted to audio vpts %" PRId64 "\n", this->video_vpts); } else { - /* video */ - if (this->audio_vpts < cur_time) { - /* video, no sound */ - this->audio_vpts = this->video_vpts; - this->audio_vpts_rmndr = 0; - xprintf(this->xine, XINE_VERBOSITY_DEBUG, "audio vpts adjusted to video vpts %" PRId64 "\n", this->video_vpts); - } else { - /* video + audio */ - } + this->audio_vpts = this->video_vpts; + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "audio vpts adjusted to video vpts %" PRId64 "\n", this->video_vpts); } break; } @@ -373,7 +354,7 @@ static void metronom_handle_discontinuity (metronom_t *this, int type, this->vpts_offset = this->video_vpts - disc_off; break; } - + this->last_video_pts = 0; this->last_audio_pts = 0; } -- cgit v1.2.3 From 52e850144c2764c73ffdc10fc46bae1a921fc445 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Thu, 10 Jan 2008 00:03:00 +0100 Subject: Added new buffer flag for audio padding. --- src/xine-engine/buffer.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/xine-engine/buffer.h b/src/xine-engine/buffer.h index 35ab1e620..b8ae1afc3 100644 --- a/src/xine-engine/buffer.h +++ b/src/xine-engine/buffer.h @@ -372,6 +372,13 @@ struct buf_element_s { * decoder_info[2] carries denominator for display aspect ratio */ #define BUF_FLAG_ASPECT 0x0800 +/* Amount of audio padding added by encoder (mp3, aac). These empty + * audio frames are causing a gap when switching between mp3 files. + * decoder_info[1] carries amount of audio frames padded at the + * beginning of the buffer + * decoder_info[2] carries amount of audio frames padded at the end of + * the buffer */ +#define BUF_FLAG_AUDIO_PADDING 0x1000 /* Special buffer types: * Sometimes there is a need to relay special information from a demuxer -- cgit v1.2.3 From 9a73bf70505fabfa0e96dec07f20337286270385 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Thu, 10 Jan 2008 00:05:14 +0100 Subject: Added audio padding handling. Fixed pts handling. --- src/libmad/xine_mad_decoder.c | 67 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/libmad/xine_mad_decoder.c b/src/libmad/xine_mad_decoder.c index 2f6d7cfe3..bb4ba1f7a 100644 --- a/src/libmad/xine_mad_decoder.c +++ b/src/libmad/xine_mad_decoder.c @@ -70,6 +70,9 @@ typedef struct mad_decoder_s { uint8_t buffer[INPUT_BUF_SIZE]; int bytes_in_buffer; int preview_mode; + int start_padding; + int end_padding; + int needs_more_data; } mad_decoder_t; @@ -84,6 +87,9 @@ static void mad_reset (audio_decoder_t *this_gen) { this->pts = 0; this->bytes_in_buffer = 0; this->preview_mode = 0; + this->start_padding = 0; + this->end_padding = 0; + this->needs_more_data = 0; mad_synth_init (&this->synth); mad_stream_init (&this->stream); @@ -95,7 +101,7 @@ static void mad_reset (audio_decoder_t *this_gen) { static void mad_discontinuity (audio_decoder_t *this_gen) { mad_decoder_t *this = (mad_decoder_t *) this_gen; - + this->pts = 0; } @@ -136,7 +142,7 @@ static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { mad_decoder_t *this = (mad_decoder_t *) this_gen; - lprintf ("decode data, decoder_flags: %d\n", buf->decoder_flags); + lprintf ("decode data, decoder_flags: 0x%08X\n", buf->decoder_flags); if (buf->size>(INPUT_BUF_SIZE-this->bytes_in_buffer)) { xprintf (this->xstream->xine, XINE_VERBOSITY_DEBUG, @@ -144,7 +150,7 @@ static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { buf->size, INPUT_BUF_SIZE-this->bytes_in_buffer); buf->size = INPUT_BUF_SIZE-this->bytes_in_buffer; } - + if ((buf->decoder_flags & BUF_FLAG_HEADER) == 0) { /* reset decoder on leaving preview mode */ @@ -167,6 +173,17 @@ static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { mad_stream_buffer (&this->stream, this->buffer, this->bytes_in_buffer); + if (!this->needs_more_data) { + this->pts = buf->pts; + if (buf->decoder_flags & BUF_FLAG_AUDIO_PADDING) { + this->start_padding = buf->decoder_info[1]; + this->end_padding = buf->decoder_info[2]; + } else { + this->start_padding = 0; + this->end_padding = 0; + } + } + while (1) { if (mad_frame_decode (&this->frame, &this->stream) != 0) { @@ -184,6 +201,8 @@ static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { switch (this->stream.error) { case MAD_ERROR_BUFLEN: + /* libmad wants more data */ + this->needs_more_data = 1; return; default: @@ -247,13 +266,14 @@ static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { mad_synth_frame (&this->synth, &this->frame); if ( (buf->decoder_flags & BUF_FLAG_PREVIEW) == 0 ) { - + unsigned int nchannels, nsamples; mad_fixed_t const *left_ch, *right_ch; struct mad_pcm *pcm = &this->synth.pcm; audio_buffer_t *audio_buffer; uint16_t *output; + audio_buffer = this->xstream->audio_out->get_buffer (this->xstream->audio_out); output = audio_buffer->mem; @@ -261,7 +281,24 @@ static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { nsamples = pcm->length; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; - + + /* padding */ + if (this->start_padding || this->end_padding) { + /* check padding validity */ + if (nsamples < (this->start_padding + this->end_padding)) { + lprintf("invalid padding data"); + this->start_padding = 0; + this->end_padding = 0; + } + lprintf("nsamples=%d, start_padding=%d, end_padding=%d\n", + nsamples, this->start_padding, this->end_padding); + nsamples -= this->start_padding + this->end_padding; + left_ch += this->start_padding; + right_ch += this->start_padding; + } + audio_buffer->num_frames = nsamples; + audio_buffer->vpts = this->pts; + while (nsamples--) { /* output sample(s) in 16-bit signed little-endian PCM */ @@ -272,19 +309,25 @@ static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { } - audio_buffer->num_frames = pcm->length; - audio_buffer->vpts = buf->pts; + lprintf ("audio_out->put_buffer, samples=%d, pts=%"PRId64"\n", audio_buffer->num_frames, audio_buffer->vpts); this->xstream->audio_out->put_buffer (this->xstream->audio_out, audio_buffer, this->xstream); + this->pts = buf->pts; buf->pts = 0; - + if (buf->decoder_flags & BUF_FLAG_AUDIO_PADDING) { + this->start_padding = buf->decoder_info[1]; + this->end_padding = buf->decoder_info[2]; + buf->decoder_info[1] = 0; + buf->decoder_info[2] = 0; + } else { + this->start_padding = 0; + this->end_padding = 0; + } } - - lprintf ("decode worked\n"); + lprintf ("decode worked\n"); } - } - + } } } -- cgit v1.2.3