From 670812a44a3aed7f142548c2cfa5a5b36381b1a8 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sat, 22 Dec 2018 20:18:06 -0500 Subject: Support MP3 audiostreams embedded in ASF containers. Full credit to Igor Poretsky Change-Id: I54769e33665cada1e1e0ef3a5511b56c8e1b859a --- lib/rbcodec/codecs/codecs.make | 2 +- lib/rbcodec/codecs/libasf/asf.h | 1 + lib/rbcodec/codecs/mpa.c | 162 ++++++++++++++++++++++++++++++++++------ lib/rbcodec/metadata/asf.c | 5 ++ lib/rbcodec/metadata/metadata.h | 3 + 5 files changed, 151 insertions(+), 22 deletions(-) diff --git a/lib/rbcodec/codecs/codecs.make b/lib/rbcodec/codecs/codecs.make index 187987ca3c..2c56d6da3f 100644 --- a/lib/rbcodec/codecs/codecs.make +++ b/lib/rbcodec/codecs/codecs.make @@ -155,7 +155,7 @@ $(CODECLINK_LDS): $(CODEC_LDS) $(CONFIGFILE) # codec/library dependencies $(CODECDIR)/spc.codec : $(CODECDIR)/libspc.a -$(CODECDIR)/mpa.codec : $(CODECDIR)/libmad.a +$(CODECDIR)/mpa.codec : $(CODECDIR)/libmad.a $(CODECDIR)/libasf.a $(CODECDIR)/a52.codec : $(CODECDIR)/liba52.a $(CODECDIR)/flac.codec : $(CODECDIR)/libffmpegFLAC.a $(CODECDIR)/vorbis.codec : $(CODECDIR)/libtremor.a $(TLSFLIB) $(SETJMPLIB) diff --git a/lib/rbcodec/codecs/libasf/asf.h b/lib/rbcodec/codecs/libasf/asf.h index 2398a44eab..ab5de3c6c6 100644 --- a/lib/rbcodec/codecs/libasf/asf.h +++ b/lib/rbcodec/codecs/libasf/asf.h @@ -8,6 +8,7 @@ #define ASF_CODEC_ID_WMAV2 0x161 #define ASF_CODEC_ID_WMAPRO 0x162 #define ASF_CODEC_ID_WMAVOICE 0x00A +#define ASF_CODEC_ID_MP3 0x055 enum asf_error_e { ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ diff --git a/lib/rbcodec/codecs/mpa.c b/lib/rbcodec/codecs/mpa.c index e6662ddcbe..d6bcc04910 100644 --- a/lib/rbcodec/codecs/mpa.c +++ b/lib/rbcodec/codecs/mpa.c @@ -20,6 +20,7 @@ ****************************************************************************/ #include "codeclib.h" +#include "libasf/asf.h" #include #include @@ -56,6 +57,91 @@ static unsigned char mad_main_data[MAD_BUFFER_MDLEN] IBSS_ATTR; static int mpeg_latency[3] = { 0, 481, 529 }; static int mpeg_framesize[3] = {384, 1152, 1152}; +static unsigned char stream_buffer[INPUT_CHUNK_SIZE] IBSS_ATTR; +static unsigned char *stream_data_start; +static unsigned char *stream_data_end; +static unsigned char *packetdata; +static int stream_data_available; +static int packetlength; +static int packetdatasize; +static int packetrest; +static unsigned char *lastpacketpos; + +static void reset_stream_buffer(void) +{ + stream_data_start = stream_buffer; + stream_data_end = stream_buffer; + stream_data_available = 1; + packetdatasize = 0; + packetrest = 0; + lastpacketpos = stream_buffer; +} + +static inline unsigned char *get_stream_data(size_t *realsize, size_t reqsize) +{ + static int errcount = 0; + size_t datasize = stream_data_end - stream_data_start; + if (!ci->id3->is_asf_stream) + return ci->request_buffer(realsize, reqsize); + else if (datasize < INPUT_CHUNK_SIZE / 2) + { + if (stream_data_start < stream_data_end && stream_data_start > stream_buffer) + { + lastpacketpos -= stream_data_start - stream_buffer; + memmove(stream_buffer, stream_data_start, datasize); + stream_data_start = stream_buffer; + stream_data_end = stream_buffer + datasize; + } + while (datasize < INPUT_CHUNK_SIZE && (packetrest || stream_data_available > 0)) + { + if (packetrest && packetdata) + { + datasize = INPUT_CHUNK_SIZE - datasize; + if (datasize > (size_t)packetrest) + datasize = packetrest; + memcpy(stream_data_end, packetdata, datasize); + packetrest -= datasize; + stream_data_end += datasize; + if (packetrest) + packetdata += datasize; + else + { + ci->advance_buffer(packetlength); + lastpacketpos = stream_data_end; + } + datasize = stream_data_end - stream_data_start; + } + else + { + asf_waveformatex_t *wfx = (asf_waveformatex_t *)(ci->id3->toc); + int res = asf_read_packet(&packetdata, &packetdatasize, &packetlength, wfx); + if (res < 0) + { + if (res == ASF_ERROR_EOF) + stream_data_available = 0; + else if (++errcount > 5) + stream_data_available = -1; + } + else errcount = 0; + packetrest = packetdatasize; + } + } + } + if (packetdatasize && lastpacketpos > stream_data_start) + ci->set_offset(ci->curpos - ((lastpacketpos - stream_data_start) / packetdatasize + 1) * packetlength); + *realsize = (datasize > reqsize) ? reqsize : datasize; + return stream_data_start; +} + +static void advance_stream_buffer(size_t size) +{ + if (!ci->id3->is_asf_stream) + ci->advance_buffer(size); + else if (stream_data_start + size > stream_data_end) + stream_data_start = stream_data_end; + else stream_data_start += size; +} + static void init_mad(void) { ci->memset(&stream, 0, sizeof(struct mad_stream)); @@ -346,14 +432,26 @@ enum codec_status codec_run(void) current_frequency = ci->id3->frequency; codec_set_replaygain(ci->id3); - if (!ci->id3->offset && ci->id3->elapsed) { + if (!ci->id3->is_asf_stream && !ci->id3->offset && ci->id3->elapsed) { /* Have elapsed time but not offset */ ci->id3->offset = get_file_pos(ci->id3->elapsed); } if (ci->id3->offset) { - ci->seek_buffer(ci->id3->offset); - set_elapsed(ci->id3); + + if (ci->id3->is_asf_stream) { + asf_waveformatex_t *wfx = (asf_waveformatex_t *)(ci->id3->toc); + int packet_offset = ((ci->id3->offset > ci->id3->first_frame_offset) ? + (ci->id3->offset - ci->id3->first_frame_offset) : 0) + % wfx->packet_size; + ci->seek_buffer(ci->id3->offset - packet_offset); + ci->id3->elapsed = asf_get_timestamp(&packet_offset); + ci->set_elapsed(ci->id3->elapsed); + } + else { + ci->seek_buffer(ci->id3->offset); + set_elapsed(ci->id3); + } } else ci->seek_buffer(ci->id3->first_frame_offset); @@ -388,6 +486,8 @@ enum codec_status codec_run(void) else samples_to_skip = start_skip; + if (ci->id3->is_asf_stream) + reset_stream_buffer(); framelength = 0; /* This is the decoding loop. */ @@ -398,39 +498,59 @@ enum codec_status codec_run(void) break; if (action == CODEC_ACTION_SEEK_TIME) { - int newpos; - /*make sure the synth thread is idle before seeking - MT only*/ mad_synth_thread_wait_pcm(); mad_synth_thread_unwait_pcm(); - samplesdone = ((int64_t)param)*current_frequency/1000; - if (param == 0) { - newpos = ci->id3->first_frame_offset; samples_to_skip = start_skip; } else { - newpos = get_file_pos(param); samples_to_skip = 0; } - if (!ci->seek_buffer(newpos)) - { + if (ci->id3->is_asf_stream) { + asf_waveformatex_t *wfx = (asf_waveformatex_t *)(ci->id3->toc); + int elapsedtime = asf_seek(param, wfx); + + samplesdone = (elapsedtime > 0) ? + (((int64_t)elapsedtime)*current_frequency/1000) : 0; + + if (elapsedtime < 1) { + ci->set_elapsed(0); + ci->seek_complete(); + break; + } else { + ci->set_elapsed(elapsedtime); + ci->seek_complete(); + reset_stream_buffer(); + } + } else { + int newpos = param ? get_file_pos(param) : (int)(ci->id3->first_frame_offset); + + samplesdone = ((int64_t)param)*current_frequency/1000; + + if (!ci->seek_buffer(newpos)) + { + ci->seek_complete(); + break; + } + + ci->set_elapsed((samplesdone * 1000LL) / current_frequency); ci->seek_complete(); - break; } - ci->set_elapsed((samplesdone * 1000LL) / current_frequency); - ci->seek_complete(); init_mad(); framelength = 0; } /* Lock buffers */ if (stream.error == 0) { - inputbuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE); - if (size == 0 || inputbuffer == NULL) + inputbuffer = get_stream_data(&size, INPUT_CHUNK_SIZE); + if (size == 0 || inputbuffer == NULL) { + if (ci->id3->is_asf_stream && stream_data_available < 0) + return CODEC_ERROR; break; + } mad_stream_buffer(&stream, (unsigned char *)inputbuffer, size + padding); } @@ -443,9 +563,9 @@ enum codec_status codec_run(void) /* Fill the buffer */ if (stream.next_frame) - ci->advance_buffer(stream.next_frame - stream.buffer); + advance_stream_buffer(stream.next_frame - stream.buffer); else - ci->advance_buffer(size); + advance_stream_buffer(size); stream.error = 0; /* Must get new inputbuffer next time */ file_end++; continue; @@ -454,7 +574,7 @@ enum codec_status codec_run(void) continue; } else { /* Some other unrecoverable error */ - return CODEC_ERROR; + return CODEC_ERROR; } } @@ -495,9 +615,9 @@ enum codec_status codec_run(void) } if (stream.next_frame) - ci->advance_buffer(stream.next_frame - stream.buffer); + advance_stream_buffer(stream.next_frame - stream.buffer); else - ci->advance_buffer(size); + advance_stream_buffer(size); stream.error = 0; /* Must get new inputbuffer next time */ file_end = 0; diff --git a/lib/rbcodec/metadata/asf.c b/lib/rbcodec/metadata/asf.c index 0d115099ec..9a74ada453 100644 --- a/lib/rbcodec/metadata/asf.c +++ b/lib/rbcodec/metadata/asf.c @@ -375,6 +375,11 @@ static int asf_parse_header(int fd, struct mp3entry* id3, lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); wfx->audiostream = flags&0x7f; id3->codectype = AFMT_WMAVOICE; + } else if (wfx->codec_id == ASF_CODEC_ID_MP3) { + lseek(fd,current.size - 24 - 72,SEEK_CUR); + wfx->audiostream = flags&0x7f; + id3->codectype = AFMT_MPA_L3; + id3->is_asf_stream = true; } else { DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); lseek(fd,current.size - 24 - 72,SEEK_CUR); diff --git a/lib/rbcodec/metadata/metadata.h b/lib/rbcodec/metadata/metadata.h index fc9c1d062c..768e62d3b0 100644 --- a/lib/rbcodec/metadata/metadata.h +++ b/lib/rbcodec/metadata/metadata.h @@ -316,6 +316,9 @@ struct mp3entry { /* Musicbrainz Track ID */ char* mb_track_id; + + /* For ASF files with MP3 audio stream */ + bool is_asf_stream; }; unsigned int probe_file_format(const char *filename); -- cgit v1.2.3