mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-28 00:06:20 -04:00
Support MP3 audiostreams embedded in ASF containers.
Full credit to Igor Poretsky Change-Id: I54769e33665cada1e1e0ef3a5511b56c8e1b859a
This commit is contained in:
parent
13c7f482ce
commit
670812a44a
5 changed files with 151 additions and 22 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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,14 +432,26 @@ 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) {
|
||||||
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
|
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,39 +498,59 @@ 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->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();
|
ci->seek_complete();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->set_elapsed((samplesdone * 1000LL) / current_frequency);
|
|
||||||
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;
|
||||||
|
|
@ -454,7 +574,7 @@ enum codec_status codec_run(void)
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
/* Some other unrecoverable error */
|
/* Some other unrecoverable error */
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue