Realmedia related codecs fixes and enhancements

* More tolerance to the file format variations.
 * AC3 coded files in realaudio format are now playable

Full credit to Igor Poretsky

Change-Id: Id24e94bc00623e89fb8c80403efa92f69ab1e5d7
This commit is contained in:
Solomon Peachy 2018-12-22 20:04:28 -05:00
parent eee3f0ce79
commit 9b9b30bd54
8 changed files with 309 additions and 124 deletions

View file

@ -44,6 +44,14 @@ static void init_rm(RMContext *rmctx)
/* used outside liba52 */ /* used outside liba52 */
static uint8_t buf[3840] IBSS_ATTR; static uint8_t buf[3840] IBSS_ATTR;
static uint8_t *bufptr = buf;
static uint8_t *bufpos = buf + 7;
static void a52_decoder_reset(void)
{
bufptr = buf;
bufpos = buf + 7;
}
/* The following two functions, a52_decode_data and output_audio are taken from a52.c */ /* The following two functions, a52_decode_data and output_audio are taken from a52.c */
static inline void output_audio(sample_t *samples) static inline void output_audio(sample_t *samples)
@ -52,10 +60,8 @@ static inline void output_audio(sample_t *samples)
ci->pcmbuf_insert(&samples[0], &samples[256], 256); ci->pcmbuf_insert(&samples[0], &samples[256], 256);
} }
static void a52_decode_data(uint8_t *start, uint8_t *end) static size_t a52_decode_data(uint8_t *start, uint8_t *end)
{ {
static uint8_t *bufptr = buf;
static uint8_t *bufpos = buf + 7;
/* /*
* sample_rate and flags are static because this routine could * sample_rate and flags are static because this routine could
* exit between the a52_syncinfo() and the ao_setup(), and we want * exit between the a52_syncinfo() and the ao_setup(), and we want
@ -65,6 +71,7 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
static int flags; static int flags;
int bit_rate; int bit_rate;
int len; int len;
size_t consumed = 0;
while (1) { while (1) {
len = end - start; len = end - start;
@ -75,6 +82,7 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
memcpy(bufptr, start, len); memcpy(bufptr, start, len);
bufptr += len; bufptr += len;
start += len; start += len;
consumed += len;
if (bufptr == bufpos) { if (bufptr == bufpos) {
if (bufpos == buf + 7) { if (bufpos == buf + 7) {
int length; int length;
@ -114,7 +122,7 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
ci->set_elapsed(samplesdone/(frequency/1000)); ci->set_elapsed(samplesdone/(frequency/1000));
bufptr = buf; bufptr = buf;
bufpos = buf + 7; bufpos = buf + 7;
continue; break;
error: error:
//logf("Error decoding A52 stream\n"); //logf("Error decoding A52 stream\n");
bufptr = buf; bufptr = buf;
@ -122,6 +130,7 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
} }
} }
} }
return consumed;
} }
/* this is the codec entry point */ /* this is the codec entry point */
@ -143,11 +152,13 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
/* this is called for each file to process */ /* this is called for each file to process */
enum codec_status codec_run(void) enum codec_status codec_run(void)
{ {
size_t n; size_t consumed = 0, n = 0;
uint8_t *filebuf; uint8_t *filebuf;
int consumed, packet_offset; int packet_offset;
int playback_on = -1; int playback_on = -1;
size_t resume_offset; size_t resume_offset;
size_t data_offset;
size_t packet_size;
long action; long action;
intptr_t param; intptr_t param;
@ -162,21 +173,28 @@ enum codec_status codec_run(void)
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
codec_set_replaygain(ci->id3); codec_set_replaygain(ci->id3);
ci->seek_buffer(ci->id3->first_frame_offset); ci->seek_buffer(0);
/* Intializations */ /* Initializations */
state = a52_init(0); state = a52_init(0);
ci->memset(&rmctx,0,sizeof(RMContext)); ci->memset(&rmctx,0,sizeof(RMContext));
ci->memset(&pkt,0,sizeof(RMPacket)); ci->memset(&pkt,0,sizeof(RMPacket));
init_rm(&rmctx); init_rm(&rmctx);
data_offset = rmctx.data_offset +
((rmctx.flags & RM_RAW_DATASTREAM) ? 0 : DATA_HEADER_SIZE);
packet_size = rmctx.block_align +
((rmctx.flags & RM_RAW_DATASTREAM) ?
0 :
(PACKET_HEADER_SIZE +
((rmctx.flags & RM_PKT_V1) ? 1 : 0)));
samplesdone = 0; samplesdone = 0;
/* check for a mid-track resume and force a seek time accordingly */ /* check for a mid-track resume and force a seek time accordingly */
if (resume_offset) { if (resume_offset) {
resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE); resume_offset -= MIN(resume_offset, data_offset);
/* put number of subpackets to skip in resume_offset */ /* put number of subpackets to skip in resume_offset */
resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); resume_offset /= packet_size;
param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
} }
@ -186,11 +204,11 @@ enum codec_status codec_run(void)
else { else {
/* Seek to the first packet */ /* Seek to the first packet */
ci->set_elapsed(0); ci->set_elapsed(0);
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); ci->advance_buffer(data_offset);
} }
/* The main decoding loop */ /* The main decoding loop */
while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) { while ((rmctx.flags & RM_RAW_DATASTREAM) || (unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) {
if (action == CODEC_ACTION_NULL) if (action == CODEC_ACTION_NULL)
action = ci->get_command(&param); action = ci->get_command(&param);
@ -198,21 +216,52 @@ enum codec_status codec_run(void)
break; break;
if (action == CODEC_ACTION_SEEK_TIME) { if (action == CODEC_ACTION_SEEK_TIME) {
packet_offset = param / ((rmctx.block_align*8*1000)/rmctx.bit_rate); /* Do not allow seeking beyond the file's length */
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + if ((unsigned) param > ci->id3->length) {
packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE)); ci->set_elapsed(ci->id3->length);
rmctx.audio_pkt_cnt = packet_offset;
samplesdone = (rmctx.sample_rate/1000 * param);
ci->set_elapsed(samplesdone/(frequency/1000));
ci->seek_complete(); ci->seek_complete();
break;
}
if (n)
rm_ac3_swap_bytes(filebuf, (rmctx.flags & RM_RAW_DATASTREAM) ? n : rmctx.block_align);
packet_offset = param / ((rmctx.block_align*8*1000)/rmctx.bit_rate);
ci->seek_buffer(data_offset + packet_offset * packet_size);
rmctx.audio_pkt_cnt = packet_offset;
samplesdone = packet_offset * A52_SAMPLESPERFRAME;
ci->set_elapsed(samplesdone/(ci->id3->frequency/1000));
ci->seek_complete();
a52_decoder_reset();
consumed = 0;
n = 0;
} }
action = CODEC_ACTION_NULL; action = CODEC_ACTION_NULL;
filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE); if (rmctx.flags & RM_RAW_DATASTREAM) {
consumed = rm_get_packet(&filebuf, &rmctx, &pkt); if (n > consumed) {
consumed += a52_decode_data(filebuf + consumed, filebuf + n);
ci->set_offset(ci->curpos + consumed);
}
else {
if (n) {
rm_ac3_swap_bytes(filebuf, n);
ci->advance_buffer(n);
}
filebuf = ci->request_buffer(&n, BUFFER_SIZE);
if (n == 0)
break;
rm_ac3_swap_bytes(filebuf, n);
consumed = 0;
}
}
else {
filebuf = ci->request_buffer(&n, packet_size);
if(consumed < 0 && playback_on != 0) { if (n == 0)
break;
if (rm_get_packet(&filebuf, &rmctx, &pkt) < 0 && playback_on != 0) {
if(playback_on == -1) { if(playback_on == -1) {
/* Error only if packet-parsing failed and playback hadn't started */ /* Error only if packet-parsing failed and playback hadn't started */
DEBUGF("rm_get_packet failed\n"); DEBUGF("rm_get_packet failed\n");
@ -224,9 +273,13 @@ enum codec_status codec_run(void)
} }
playback_on = 1; playback_on = 1;
a52_decode_data(filebuf, filebuf + rmctx.block_align); consumed = 0;
while (consumed < rmctx.block_align)
consumed += a52_decode_data(filebuf + consumed, filebuf + rmctx.block_align);
rm_ac3_swap_bytes(filebuf, rmctx.block_align);
ci->advance_buffer(pkt.length); ci->advance_buffer(pkt.length);
} }
}
return CODEC_OK; return CODEC_OK;
} }

View file

@ -21,7 +21,6 @@
#include <string.h> #include <string.h>
#include "logf.h"
#include "codeclib.h" #include "codeclib.h"
#include "inttypes.h" #include "inttypes.h"
#include "libatrac/atrac3.h" #include "libatrac/atrac3.h"
@ -37,10 +36,26 @@ static void init_rm(RMContext *rmctx)
/* initialize the RMContext */ /* initialize the RMContext */
memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext));
/* and atrac3 expects extadata in id3v2buf, so we shall give it that */ /* and atrac3 expects extradata in id3v2buf, so we shall give it that */
memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char)); memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char));
} }
static int request_packet(int size)
{
int consumed = 0;
while (1)
{
uint8_t *buffer = ci->request_buffer((size_t *)(&consumed), size);
if (!consumed)
break;
consumed = rm_get_packet(&buffer, &rmctx, &pkt);
if (consumed < 0 || consumed == size)
break;
ci->advance_buffer(size);
}
return consumed;
}
/* this is the codec entry point */ /* this is the codec entry point */
enum codec_status codec_main(enum codec_entry_call_reason reason) enum codec_status codec_main(enum codec_entry_call_reason reason)
{ {
@ -52,12 +67,10 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
/* this is called for each file to process */ /* this is called for each file to process */
enum codec_status codec_run(void) enum codec_status codec_run(void)
{ {
static size_t buff_size;
int datasize, res, consumed, i, time_offset; int datasize, res, consumed, i, time_offset;
uint8_t *bit_buffer;
uint16_t fs,sps,h; uint16_t fs,sps,h;
uint32_t packet_count; uint32_t packet_count;
int scrambling_unit_size, num_units, elapsed; int spn, packet_header_size, scrambling_unit_size, num_units, elapsed;
int playback_on = -1; int playback_on = -1;
size_t resume_offset; size_t resume_offset;
intptr_t param; intptr_t param;
@ -85,13 +98,16 @@ enum codec_status codec_run(void)
ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ? ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ?
STEREO_MONO : STEREO_NONINTERLEAVED); STEREO_MONO : STEREO_NONINTERLEAVED);
packet_header_size = PACKET_HEADER_SIZE +
((rmctx.flags & RM_PKT_V1) ? 1 : 0);
packet_count = rmctx.nb_packets; packet_count = rmctx.nb_packets;
rmctx.audio_framesize = rmctx.block_align; rmctx.audio_framesize = rmctx.block_align;
rmctx.block_align = rmctx.sub_packet_size; rmctx.block_align = rmctx.sub_packet_size;
fs = rmctx.audio_framesize; fs = rmctx.audio_framesize;
sps= rmctx.block_align; sps= rmctx.block_align;
h = rmctx.sub_packet_h; h = rmctx.sub_packet_h;
scrambling_unit_size = h * (fs + PACKET_HEADER_SIZE); scrambling_unit_size = h * (fs + packet_header_size);
spn = h * fs / sps;
res = atrac3_decode_init(&q, ci->id3); res = atrac3_decode_init(&q, ci->id3);
if(res < 0) { if(res < 0) {
@ -103,9 +119,9 @@ enum codec_status codec_run(void)
if(resume_offset) { if(resume_offset) {
resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE); resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
num_units = (int)resume_offset / scrambling_unit_size; num_units = (int)resume_offset / scrambling_unit_size;
/* put number of subpackets to skip in resume_offset */ /* put number of packets to skip in resume_offset */
resume_offset /= (sps + PACKET_HEADER_SIZE); resume_offset = num_units * h;
elapsed = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); elapsed = (int)resume_offset * ((8000LL * fs)/rmctx.bit_rate);
} }
if (elapsed > 0) { if (elapsed > 0) {
@ -123,8 +139,9 @@ enum codec_status codec_run(void)
seek_start : seek_start :
while((unsigned)elapsed < rmctx.duration) while((unsigned)elapsed < rmctx.duration)
{ {
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); consumed = request_packet(scrambling_unit_size);
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); if (!consumed)
break;
if(consumed < 0 && playback_on != 0) { if(consumed < 0 && playback_on != 0) {
if(playback_on == -1) { if(playback_on == -1) {
/* Error only if packet-parsing failed and playback hadn't started */ /* Error only if packet-parsing failed and playback hadn't started */
@ -135,7 +152,7 @@ seek_start :
return CODEC_OK; return CODEC_OK;
} }
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) for (i = 0; i < spn; i++)
{ {
if (action == CODEC_ACTION_NULL) if (action == CODEC_ACTION_NULL)
action = ci->get_command(&param); action = ci->get_command(&param);
@ -164,10 +181,11 @@ seek_start :
action = CODEC_ACTION_NULL; action = CODEC_ACTION_NULL;
goto seek_start; goto seek_start;
} }
num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); num_units = (param/(sps*1000*8/rmctx.bit_rate))/spn;
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); consumed = request_packet(scrambling_unit_size);
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); if (!consumed)
return CODEC_OK;
if(consumed < 0 && playback_on != 0) { if(consumed < 0 && playback_on != 0) {
if(playback_on == -1) { if(playback_on == -1) {
/* Error only if packet-parsing failed and playback hadn't started */ /* Error only if packet-parsing failed and playback hadn't started */
@ -178,19 +196,32 @@ seek_start :
return CODEC_OK; return CODEC_OK;
} }
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; packet_count = rmctx.nb_packets - h * num_units;
rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate));
while(rmctx.audiotimestamp > (unsigned) param) { while (rmctx.audiotimestamp > (unsigned)param && num_units-- > 0) {
rmctx.audio_pkt_cnt = 0; rmctx.audio_pkt_cnt = 0;
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); consumed = request_packet(scrambling_unit_size);
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); if (!consumed)
packet_count += rmctx.audio_pkt_cnt; return CODEC_OK;
num_units--; if(consumed < 0 && playback_on != 0) {
if(playback_on == -1) {
/* Error only if packet-parsing failed and playback hadn't started */
DEBUGF("rm_get_packet failed\n");
return CODEC_ERROR;
} }
else
return CODEC_OK;
}
packet_count += h;
}
if (num_units < 0)
rmctx.audiotimestamp = 0;
time_offset = param - rmctx.audiotimestamp; time_offset = param - rmctx.audiotimestamp;
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i; elapsed = param;
ci->set_elapsed(elapsed); ci->set_elapsed(elapsed);
ci->seek_complete(); ci->seek_complete();
} }
@ -198,11 +229,11 @@ seek_start :
action = CODEC_ACTION_NULL; action = CODEC_ACTION_NULL;
if(pkt.length) if(pkt.length)
res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align); res = atrac3_decode_frame(sps, &q, &datasize, pkt.frames[i], sps);
else /* indicates that there are no remaining frames */ else /* indicates that there are no remaining frames */
return CODEC_OK; return CODEC_OK;
if(res != rmctx.block_align) { if (res != sps) {
DEBUGF("codec error\n"); DEBUGF("codec error\n");
return CODEC_ERROR; return CODEC_ERROR;
} }
@ -214,9 +245,9 @@ seek_start :
ci->set_elapsed(elapsed); ci->set_elapsed(elapsed);
rmctx.frame_number++; rmctx.frame_number++;
} }
packet_count -= rmctx.audio_pkt_cnt; packet_count -= h;
rmctx.audio_pkt_cnt = 0; rmctx.audio_pkt_cnt = 0;
ci->advance_buffer(consumed); ci->advance_buffer(scrambling_unit_size);
} }
return CODEC_OK; return CODEC_OK;

View file

@ -21,7 +21,6 @@
#include <string.h> #include <string.h>
#include "logf.h"
#include "codeclib.h" #include "codeclib.h"
#include "inttypes.h" #include "inttypes.h"
#include "libcook/cook.h" #include "libcook/cook.h"
@ -38,6 +37,22 @@ static void init_rm(RMContext *rmctx)
memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext));
} }
static int request_packet(int size)
{
int consumed = 0;
while (1)
{
uint8_t *buffer = ci->request_buffer((size_t *)(&consumed), size);
if (!consumed)
break;
consumed = rm_get_packet(&buffer, &rmctx, &pkt);
if (consumed < 0 || consumed == size)
break;
ci->advance_buffer(size);
}
return consumed;
}
/* this is the codec entry point */ /* this is the codec entry point */
enum codec_status codec_main(enum codec_entry_call_reason reason) enum codec_status codec_main(enum codec_entry_call_reason reason)
{ {
@ -49,12 +64,10 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
/* this is called for each file to process */ /* this is called for each file to process */
enum codec_status codec_run(void) enum codec_status codec_run(void)
{ {
static size_t buff_size;
int datasize, res, consumed, i, time_offset; int datasize, res, consumed, i, time_offset;
uint8_t *bit_buffer;
uint16_t fs,sps,h; uint16_t fs,sps,h;
uint32_t packet_count; uint32_t packet_count;
int scrambling_unit_size, num_units; int spn, packet_header_size, scrambling_unit_size, num_units;
size_t resume_offset; size_t resume_offset;
intptr_t param; intptr_t param;
long action; long action;
@ -84,13 +97,16 @@ enum codec_status codec_run(void)
ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ? ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ?
STEREO_MONO : STEREO_NONINTERLEAVED); STEREO_MONO : STEREO_NONINTERLEAVED);
packet_header_size = PACKET_HEADER_SIZE +
((rmctx.flags & RM_PKT_V1) ? 1 : 0);
packet_count = rmctx.nb_packets; packet_count = rmctx.nb_packets;
rmctx.audio_framesize = rmctx.block_align; rmctx.audio_framesize = rmctx.block_align;
rmctx.block_align = rmctx.sub_packet_size; rmctx.block_align = rmctx.sub_packet_size;
fs = rmctx.audio_framesize; fs = rmctx.audio_framesize;
sps= rmctx.block_align; sps= rmctx.block_align;
h = rmctx.sub_packet_h; h = rmctx.sub_packet_h;
scrambling_unit_size = h * (fs + PACKET_HEADER_SIZE); scrambling_unit_size = h * (fs + packet_header_size);
spn = h * fs / sps;
res =cook_decode_init(&rmctx, &q); res =cook_decode_init(&rmctx, &q);
if(res < 0) { if(res < 0) {
@ -102,9 +118,9 @@ enum codec_status codec_run(void)
if(resume_offset) { if(resume_offset) {
resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE); resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
num_units = (int)resume_offset / scrambling_unit_size; num_units = (int)resume_offset / scrambling_unit_size;
/* put number of subpackets to skip in resume_offset */ /* put number of packets to skip in resume_offset */
resume_offset /= (sps + PACKET_HEADER_SIZE); resume_offset = num_units * h;
param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); param = (int)resume_offset * ((8000LL * fs)/rmctx.bit_rate);
} }
if (param) { if (param) {
@ -120,14 +136,15 @@ enum codec_status codec_run(void)
seek_start : seek_start :
while(packet_count) while(packet_count)
{ {
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); consumed = request_packet(scrambling_unit_size);
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); if (!consumed)
break;
if(consumed < 0) { if(consumed < 0) {
DEBUGF("rm_get_packet failed\n"); DEBUGF("rm_get_packet failed\n");
return CODEC_ERROR; return CODEC_ERROR;
} }
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) for (i = 0; i < spn; i++)
{ {
if (action == CODEC_ACTION_NULL) if (action == CODEC_ACTION_NULL)
action = ci->get_command(&param); action = ci->get_command(&param);
@ -155,52 +172,65 @@ seek_start :
action = CODEC_ACTION_NULL; action = CODEC_ACTION_NULL;
goto seek_start; goto seek_start;
} }
num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); num_units = (param/(sps*1000*8/rmctx.bit_rate))/spn;
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); consumed = request_packet(scrambling_unit_size);
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); if (!consumed) {
ci->seek_complete();
return CODEC_OK;
}
if(consumed < 0) { if(consumed < 0) {
DEBUGF("rm_get_packet failed\n"); DEBUGF("rm_get_packet failed\n");
ci->seek_complete(); ci->seek_complete();
return CODEC_ERROR; return CODEC_ERROR;
} }
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
packet_count = rmctx.nb_packets - h * num_units;
rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate));
while(rmctx.audiotimestamp > (unsigned) param) { while(rmctx.audiotimestamp > (unsigned)param && num_units-- > 0) {
rmctx.audio_pkt_cnt = 0; rmctx.audio_pkt_cnt = 0;
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); consumed = request_packet(scrambling_unit_size);
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); if (!consumed) {
packet_count += rmctx.audio_pkt_cnt; ci->seek_complete();
num_units--; return CODEC_OK;
} }
if(consumed < 0) {
ci->seek_complete();
DEBUGF("rm_get_packet failed\n");
return CODEC_ERROR;
}
packet_count += h;
}
if (num_units < 0)
rmctx.audiotimestamp = 0;
time_offset = param - rmctx.audiotimestamp; time_offset = param - rmctx.audiotimestamp;
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); ci->set_elapsed(param);
ci->seek_complete(); ci->seek_complete();
} }
action = CODEC_ACTION_NULL; action = CODEC_ACTION_NULL;
res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align); res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], sps);
rmctx.frame_number++;
/* skip the first two frames; no valid audio */ if (res != sps) {
if(rmctx.frame_number < 3) continue;
if(res != rmctx.block_align) {
DEBUGF("codec error\n"); DEBUGF("codec error\n");
return CODEC_ERROR; return CODEC_ERROR;
} }
if(datasize)
ci->pcmbuf_insert(rm_outbuf, ci->pcmbuf_insert(rm_outbuf,
rm_outbuf+q.samples_per_channel, rm_outbuf+q.samples_per_channel,
q.samples_per_channel); q.samples_per_channel);
ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i);
rmctx.frame_number++;
} }
packet_count -= rmctx.audio_pkt_cnt; packet_count -= h;
rmctx.audio_pkt_cnt = 0; rmctx.audio_pkt_cnt = 0;
ci->advance_buffer(consumed); ci->advance_buffer(scrambling_unit_size);
} }
return CODEC_OK; return CODEC_OK;

View file

@ -27,8 +27,6 @@
#include "codeclib.h" #include "codeclib.h"
#endif #endif
#define SWAP(a, b) do{uint8_t SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
#ifdef TEST #ifdef TEST
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
@ -500,17 +498,33 @@ void rm_get_packet_fd(int fd,RMContext *rmctx, RMPacket *pkt)
} }
#endif /*TEST*/ #endif /*TEST*/
void rm_ac3_swap_bytes(uint8_t *buf, int bufsize)
{
uint8_t *bufptr;
for (bufptr = buf; bufptr < buf + bufsize - 1; bufptr += 2)
{
bufptr[0] ^= bufptr[1];
bufptr[1] ^= bufptr[0];
bufptr[0] ^= bufptr[1];
}
}
int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt) int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt)
{ {
int consumed = 0; int consumed = 0;
int headerlen;
/* rockbox: comment 'set but unused' variables /* rockbox: comment 'set but unused' variables
uint8_t unknown; uint8_t unknown;
*/ */
uint16_t x, place; uint16_t x;
uint16_t sps = rmctx->sub_packet_size; uint16_t sps = rmctx->sub_packet_size;
uint16_t h = rmctx->sub_packet_h; uint16_t h = rmctx->sub_packet_h;
uint16_t y = rmctx->sub_packet_cnt; uint16_t y = 0;
uint16_t w = rmctx->audio_framesize; uint16_t w = rmctx->audio_framesize;
rmctx->sub_packet_cnt = 0;
rmctx->audio_pkt_cnt = 0;
do do
{ {
y = rmctx->sub_packet_cnt; y = rmctx->sub_packet_cnt;
@ -523,6 +537,7 @@ int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt)
return -1; return -1;
} }
headerlen = PACKET_HEADER_SIZE + (pkt->version ? 1 : 0);
pkt->length = rm_get_uint16be(*src+2); pkt->length = rm_get_uint16be(*src+2);
pkt->stream_number = rm_get_uint16be(*src+4); pkt->stream_number = rm_get_uint16be(*src+4);
pkt->timestamp = rm_get_uint32be(*src+6); pkt->timestamp = rm_get_uint32be(*src+6);
@ -534,21 +549,23 @@ int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt)
pkt->flags = rm_get_uint8(*src+11); pkt->flags = rm_get_uint8(*src+11);
if(pkt->version == 1) if(pkt->version == 1)
/* unknown = */ rm_get_uint8(*src+10); /* unknown = */ rm_get_uint8(*src+12);
if (pkt->flags & 2) /* keyframe */ if (pkt->flags & 2) { /* keyframe */
y = rmctx->sub_packet_cnt = 0; if (y)
return consumed;
y = 0;
}
if (!y) if (!y)
rmctx->audiotimestamp = pkt->timestamp; rmctx->audiotimestamp = pkt->timestamp;
/* Skip packet header */ /* Skip packet header */
advance_buffer(src, PACKET_HEADER_SIZE); advance_buffer(src, headerlen);
consumed += PACKET_HEADER_SIZE; consumed += headerlen;
if (rmctx->codec_type == CODEC_COOK || rmctx->codec_type == CODEC_ATRAC) { if (rmctx->codec_type == CODEC_COOK || rmctx->codec_type == CODEC_ATRAC) {
for(x = 0 ; x < w/sps; x++) for(x = 0 ; x < w/sps; x++)
{ {
place = sps*(h*x+((h+1)/2)*(y&1)+(y>>1)); pkt->frames[h*x+((h+1)/2)*(y&1)+(y>>1)] = *src;
pkt->frames[place/sps] = *src;
advance_buffer(src,sps); advance_buffer(src,sps);
consumed += sps; consumed += sps;
} }
@ -563,22 +580,22 @@ int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt)
advance_buffer(src, 2); advance_buffer(src, 2);
consumed += 2; consumed += 2;
} }
rmctx->audio_pkt_cnt = --rmctx->sub_packet_cnt; rmctx->audio_pkt_cnt = rmctx->sub_packet_cnt;
} }
break;
} }
else if (rmctx->codec_type == CODEC_AC3) { else if (rmctx->codec_type == CODEC_AC3) {
/* The byte order of the data is reversed from standard AC3 */ /* The byte order of the data is reversed from standard AC3 */
for(x = 0; x < pkt->length - PACKET_HEADER_SIZE; x+=2) { rm_ac3_swap_bytes(*src, pkt->length - headerlen);
SWAP((*src)[0], (*src)[1]); break;
*src += 2;
}
*src -= x;
} }
else return -1; /* invalid codec type */
rmctx->audio_pkt_cnt++; rmctx->audio_pkt_cnt++;
}while(++(rmctx->sub_packet_cnt) < h); }while(++(rmctx->sub_packet_cnt) < h);
return consumed; return consumed;
} }
#ifdef DEBUG #ifdef DEBUG
@ -587,6 +604,6 @@ void dump_rm_context(RMContext *rmctx)
DEBUGF("block_align = %d\n", rmctx->block_align); DEBUGF("block_align = %d\n", rmctx->block_align);
DEBUGF("nb_channels = %d\n", rmctx->nb_channels); DEBUGF("nb_channels = %d\n", rmctx->nb_channels);
DEBUGF("sample_rate = %d\n", rmctx->sample_rate); DEBUGF("sample_rate = %d\n", rmctx->sample_rate);
DEBUGF("bit_rate = %d\n", rmctx->bit_rate ); DEBUGF("bit_rate = %ld\n", rmctx->bit_rate );
} }
#endif #endif

View file

@ -25,6 +25,9 @@
#include <inttypes.h> #include <inttypes.h>
#include "bytestream.h" #include "bytestream.h"
#define RM_RAW_DATASTREAM 0x0100
#define RM_PKT_V1 0x0200
#define MAX_EXTRADATA_SIZE 16 #define MAX_EXTRADATA_SIZE 16
#define DATA_HEADER_SIZE 18 #define DATA_HEADER_SIZE 18
#define PACKET_HEADER_SIZE 12 #define PACKET_HEADER_SIZE 12
@ -86,6 +89,8 @@ typedef struct rm_context
int real_parse_header(int fd, RMContext *rmctx); int real_parse_header(int fd, RMContext *rmctx);
void rm_ac3_swap_bytes(uint8_t *buf, int bufsize);
/* Get a (sub_packet_h*frames_per_packet) number of audio frames from a memory buffer */ /* Get a (sub_packet_h*frames_per_packet) number of audio frames from a memory buffer */
int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt); int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt);

View file

@ -109,7 +109,8 @@ enum codec_status codec_run(void)
if (resume_offset) { if (resume_offset) {
resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE); resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
/* put number of subpackets to skip in resume_offset */ /* put number of subpackets to skip in resume_offset */
resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE +
((rmctx.flags & RM_PKT_V1) ? 1 : 0));
param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
} }

View file

@ -444,13 +444,13 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
/* Load codec specific track tag information and confirm the codec type. */ /* Load codec specific track tag information and confirm the codec type. */
if (!entry->parse_func) if (!entry->parse_func)
{ {
DEBUGF("nothing to parse for %s (format %s)", trackname, entry->label); DEBUGF("nothing to parse for %s (format %s)\n", trackname, entry->label);
return false; return false;
} }
if (!entry->parse_func(fd, id3)) if (!entry->parse_func(fd, id3))
{ {
DEBUGF("parsing %s failed (format: %s)", trackname, entry->label); DEBUGF("parsing %s failed (format: %s)\n", trackname, entry->label);
return false; return false;
} }

View file

@ -89,7 +89,7 @@ static char* fourcc2str(uint32_t f)
} }
#endif #endif
static inline int real_read_audio_stream_info(int fd, RMContext *rmctx) static int real_read_audio_stream_info(int fd, RMContext *rmctx)
{ {
int skipped = 0; int skipped = 0;
uint32_t version; uint32_t version;
@ -111,6 +111,7 @@ static inline int real_read_audio_stream_info(int fd, RMContext *rmctx)
DEBUGF(" version=0x%04lx\n",((version >> 16) & 0xff)); DEBUGF(" version=0x%04lx\n",((version >> 16) & 0xff));
if (((version >> 16) & 0xff) == 3) { if (((version >> 16) & 0xff) == 3) {
/* Very old version */ /* Very old version */
return -1;
} else { } else {
#ifdef SIMULATOR #ifdef SIMULATOR
real_read_object_header(fd, &obj); real_read_object_header(fd, &obj);
@ -218,7 +219,7 @@ static inline int real_read_audio_stream_info(int fd, RMContext *rmctx)
return skipped; return skipped;
} }
static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3) static inline int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3)
{ {
struct real_object_t obj; struct real_object_t obj;
int res; int res;
@ -242,6 +243,7 @@ static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3)
uint32_t max_bitrate; uint32_t max_bitrate;
uint16_t num_streams; uint16_t num_streams;
uint32_t next_data_off; uint32_t next_data_off;
uint16_t pkt_version;
uint8_t header_end; uint8_t header_end;
memset(&obj,0,sizeof(obj)); memset(&obj,0,sizeof(obj));
@ -250,6 +252,35 @@ static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3)
if (obj.fourcc == FOURCC('.','r','a',0xfd)) if (obj.fourcc == FOURCC('.','r','a',0xfd))
{ {
lseek(fd, 4, SEEK_SET);
skipped = real_read_audio_stream_info(fd, rmctx);
if (skipped > 0 && rmctx->codec_type == CODEC_AC3)
{
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[0], sizeof(id3->id3v1buf[0]), '\0', len);
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[1], sizeof(id3->id3v1buf[1]), '\0', len);
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[2], sizeof(id3->id3v1buf[2]), '\0', len);
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[3], sizeof(id3->id3v1buf[3]), '\0', len);
rmctx->data_offset = skipped + 8;
rmctx->bit_rate = rmctx->block_align * rmctx->sample_rate / 192;
if (rmctx->block_align)
rmctx->nb_packets = (filesize(fd) - rmctx->data_offset) / rmctx->block_align;
if (rmctx->sample_rate)
rmctx->duration = (uint32_t)(256LL * 6 * 1000 * rmctx->nb_packets / rmctx->sample_rate);
rmctx->flags |= RM_RAW_DATASTREAM;
DEBUGF(" data_offset = %ld\n",rmctx->data_offset);
DEBUGF(" avg_bitrate = %ld\n",rmctx->bit_rate);
DEBUGF(" duration = %ld\n",rmctx->duration);
DEBUGF(" title=\"%s\"\n",id3->id3v1buf[0]);
DEBUGF(" author=\"%s\"\n",id3->id3v1buf[1]);
DEBUGF(" copyright=\"%s\"\n",id3->id3v1buf[2]);
DEBUGF(" comment=\"%s\"\n",id3->id3v1buf[3]);
return 0;
}
/* Very old .ra format - not yet supported */ /* Very old .ra format - not yet supported */
return -1; return -1;
} }
@ -305,6 +336,8 @@ static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3)
DEBUGF(" data_offset = %ld\n",rmctx->data_offset); DEBUGF(" data_offset = %ld\n",rmctx->data_offset);
DEBUGF(" num_streams = %d\n",num_streams); DEBUGF(" num_streams = %d\n",num_streams);
DEBUGF(" flags=0x%04x\n",rmctx->flags); DEBUGF(" flags=0x%04x\n",rmctx->flags);
rmctx->flags &= 0x00FF;
break; break;
case FOURCC('C','O','N','T'): case FOURCC('C','O','N','T'):
@ -409,7 +442,22 @@ static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3)
DEBUGF(" data_nb_packets = %ld\n",rmctx->nb_packets); DEBUGF(" data_nb_packets = %ld\n",rmctx->nb_packets);
DEBUGF(" next DATA offset = %ld\n",next_data_off); DEBUGF(" next DATA offset = %ld\n",next_data_off);
if (!next_data_off)
{
if (rmctx->duration == 0 && rmctx->bit_rate != 0)
{
rmctx->duration = (uint32_t)(8000LL * rmctx->nb_packets * rmctx->block_align / rmctx->bit_rate);
DEBUGF(" estimated duration = %ld\n",rmctx->duration);
}
read_uint16be(fd, &pkt_version);
skipped += 2;
DEBUGF(" pkt_version=0x%04x\n", pkt_version);
if (pkt_version)
rmctx->flags |= RM_PKT_V1;
rmctx->data_offset = curpos;
header_end = 1; header_end = 1;
}
break; break;
} }
if(header_end) break; if(header_end) break;
@ -456,7 +504,7 @@ bool get_rm_metadata(int fd, struct mp3entry* id3)
id3->channels = rmctx->nb_channels; id3->channels = rmctx->nb_channels;
id3->extradata_size = rmctx->extradata_size; id3->extradata_size = rmctx->extradata_size;
id3->bitrate = rmctx->bit_rate / 1000; id3->bitrate = (rmctx->bit_rate + 500) / 1000;
id3->frequency = rmctx->sample_rate; id3->frequency = rmctx->sample_rate;
id3->length = rmctx->duration; id3->length = rmctx->duration;
id3->filesize = filesize(fd); id3->filesize = filesize(fd);