Support MP3 audiostreams embedded in ASF containers.

Full credit to Igor Poretsky

Change-Id: I54769e33665cada1e1e0ef3a5511b56c8e1b859a
This commit is contained in:
Solomon Peachy 2018-12-22 20:18:06 -05:00
parent 13c7f482ce
commit 670812a44a
5 changed files with 151 additions and 22 deletions

View file

@ -155,7 +155,7 @@ $(CODECLINK_LDS): $(CODEC_LDS) $(CONFIGFILE)
# codec/library dependencies # codec/library dependencies
$(CODECDIR)/spc.codec : $(CODECDIR)/libspc.a $(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)/a52.codec : $(CODECDIR)/liba52.a
$(CODECDIR)/flac.codec : $(CODECDIR)/libffmpegFLAC.a $(CODECDIR)/flac.codec : $(CODECDIR)/libffmpegFLAC.a
$(CODECDIR)/vorbis.codec : $(CODECDIR)/libtremor.a $(TLSFLIB) $(SETJMPLIB) $(CODECDIR)/vorbis.codec : $(CODECDIR)/libtremor.a $(TLSFLIB) $(SETJMPLIB)

View file

@ -8,6 +8,7 @@
#define ASF_CODEC_ID_WMAV2 0x161 #define ASF_CODEC_ID_WMAV2 0x161
#define ASF_CODEC_ID_WMAPRO 0x162 #define ASF_CODEC_ID_WMAPRO 0x162
#define ASF_CODEC_ID_WMAVOICE 0x00A #define ASF_CODEC_ID_WMAVOICE 0x00A
#define ASF_CODEC_ID_MP3 0x055
enum asf_error_e { enum asf_error_e {
ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */

View file

@ -20,6 +20,7 @@
****************************************************************************/ ****************************************************************************/
#include "codeclib.h" #include "codeclib.h"
#include "libasf/asf.h"
#include <codecs/libmad/mad.h> #include <codecs/libmad/mad.h>
#include <inttypes.h> #include <inttypes.h>
@ -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_latency[3] = { 0, 481, 529 };
static int mpeg_framesize[3] = {384, 1152, 1152}; 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) static void init_mad(void)
{ {
ci->memset(&stream, 0, sizeof(struct mad_stream)); ci->memset(&stream, 0, sizeof(struct mad_stream));
@ -346,15 +432,27 @@ enum codec_status codec_run(void)
current_frequency = ci->id3->frequency; current_frequency = ci->id3->frequency;
codec_set_replaygain(ci->id3); 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 */ /* Have elapsed time but not offset */
ci->id3->offset = get_file_pos(ci->id3->elapsed); ci->id3->offset = get_file_pos(ci->id3->elapsed);
} }
if (ci->id3->offset) { if (ci->id3->offset) {
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); ci->seek_buffer(ci->id3->offset);
set_elapsed(ci->id3); set_elapsed(ci->id3);
} }
}
else else
ci->seek_buffer(ci->id3->first_frame_offset); ci->seek_buffer(ci->id3->first_frame_offset);
@ -388,6 +486,8 @@ enum codec_status codec_run(void)
else else
samples_to_skip = start_skip; samples_to_skip = start_skip;
if (ci->id3->is_asf_stream)
reset_stream_buffer();
framelength = 0; framelength = 0;
/* This is the decoding loop. */ /* This is the decoding loop. */
@ -398,22 +498,37 @@ enum codec_status codec_run(void)
break; break;
if (action == CODEC_ACTION_SEEK_TIME) { if (action == CODEC_ACTION_SEEK_TIME) {
int newpos;
/*make sure the synth thread is idle before seeking - MT only*/ /*make sure the synth thread is idle before seeking - MT only*/
mad_synth_thread_wait_pcm(); mad_synth_thread_wait_pcm();
mad_synth_thread_unwait_pcm(); mad_synth_thread_unwait_pcm();
samplesdone = ((int64_t)param)*current_frequency/1000;
if (param == 0) { if (param == 0) {
newpos = ci->id3->first_frame_offset;
samples_to_skip = start_skip; samples_to_skip = start_skip;
} else { } else {
newpos = get_file_pos(param);
samples_to_skip = 0; samples_to_skip = 0;
} }
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)) if (!ci->seek_buffer(newpos))
{ {
ci->seek_complete(); ci->seek_complete();
@ -422,15 +537,20 @@ enum codec_status codec_run(void)
ci->set_elapsed((samplesdone * 1000LL) / current_frequency); ci->set_elapsed((samplesdone * 1000LL) / current_frequency);
ci->seek_complete(); ci->seek_complete();
}
init_mad(); init_mad();
framelength = 0; framelength = 0;
} }
/* Lock buffers */ /* Lock buffers */
if (stream.error == 0) { if (stream.error == 0) {
inputbuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE); inputbuffer = get_stream_data(&size, INPUT_CHUNK_SIZE);
if (size == 0 || inputbuffer == NULL) if (size == 0 || inputbuffer == NULL) {
if (ci->id3->is_asf_stream && stream_data_available < 0)
return CODEC_ERROR;
break; break;
}
mad_stream_buffer(&stream, (unsigned char *)inputbuffer, mad_stream_buffer(&stream, (unsigned char *)inputbuffer,
size + padding); size + padding);
} }
@ -443,9 +563,9 @@ enum codec_status codec_run(void)
/* Fill the buffer */ /* Fill the buffer */
if (stream.next_frame) if (stream.next_frame)
ci->advance_buffer(stream.next_frame - stream.buffer); advance_stream_buffer(stream.next_frame - stream.buffer);
else else
ci->advance_buffer(size); advance_stream_buffer(size);
stream.error = 0; /* Must get new inputbuffer next time */ stream.error = 0; /* Must get new inputbuffer next time */
file_end++; file_end++;
continue; continue;
@ -495,9 +615,9 @@ enum codec_status codec_run(void)
} }
if (stream.next_frame) if (stream.next_frame)
ci->advance_buffer(stream.next_frame - stream.buffer); advance_stream_buffer(stream.next_frame - stream.buffer);
else else
ci->advance_buffer(size); advance_stream_buffer(size);
stream.error = 0; /* Must get new inputbuffer next time */ stream.error = 0; /* Must get new inputbuffer next time */
file_end = 0; file_end = 0;

View file

@ -375,6 +375,11 @@ static int asf_parse_header(int fd, struct mp3entry* id3,
lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR);
wfx->audiostream = flags&0x7f; wfx->audiostream = flags&0x7f;
id3->codectype = AFMT_WMAVOICE; 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 { } else {
DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n");
lseek(fd,current.size - 24 - 72,SEEK_CUR); lseek(fd,current.size - 24 - 72,SEEK_CUR);

View file

@ -316,6 +316,9 @@ struct mp3entry {
/* Musicbrainz Track ID */ /* Musicbrainz Track ID */
char* mb_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); unsigned int probe_file_format(const char *filename);