mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-11-09 21:22:39 -05:00
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:
parent
eee3f0ce79
commit
9b9b30bd54
8 changed files with 309 additions and 124 deletions
|
|
@ -44,6 +44,14 @@ static void init_rm(RMContext *rmctx)
|
|||
|
||||
/* used outside liba52 */
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
* 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;
|
||||
int bit_rate;
|
||||
int len;
|
||||
size_t consumed = 0;
|
||||
|
||||
while (1) {
|
||||
len = end - start;
|
||||
|
|
@ -75,6 +82,7 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
|
|||
memcpy(bufptr, start, len);
|
||||
bufptr += len;
|
||||
start += len;
|
||||
consumed += len;
|
||||
if (bufptr == bufpos) {
|
||||
if (bufpos == buf + 7) {
|
||||
int length;
|
||||
|
|
@ -114,7 +122,7 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
|
|||
ci->set_elapsed(samplesdone/(frequency/1000));
|
||||
bufptr = buf;
|
||||
bufpos = buf + 7;
|
||||
continue;
|
||||
break;
|
||||
error:
|
||||
//logf("Error decoding A52 stream\n");
|
||||
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 */
|
||||
|
|
@ -143,11 +152,13 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
|
|||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
size_t n;
|
||||
size_t consumed = 0, n = 0;
|
||||
uint8_t *filebuf;
|
||||
int consumed, packet_offset;
|
||||
int packet_offset;
|
||||
int playback_on = -1;
|
||||
size_t resume_offset;
|
||||
size_t data_offset;
|
||||
size_t packet_size;
|
||||
long action;
|
||||
intptr_t param;
|
||||
|
||||
|
|
@ -162,21 +173,28 @@ enum codec_status codec_run(void)
|
|||
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
ci->seek_buffer(0);
|
||||
|
||||
/* Intializations */
|
||||
/* Initializations */
|
||||
state = a52_init(0);
|
||||
ci->memset(&rmctx,0,sizeof(RMContext));
|
||||
ci->memset(&pkt,0,sizeof(RMPacket));
|
||||
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;
|
||||
|
||||
/* check for a mid-track resume and force a seek time accordingly */
|
||||
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 */
|
||||
resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
|
||||
resume_offset /= packet_size;
|
||||
param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
|
||||
}
|
||||
|
||||
|
|
@ -186,11 +204,11 @@ enum codec_status codec_run(void)
|
|||
else {
|
||||
/* Seek to the first packet */
|
||||
ci->set_elapsed(0);
|
||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE );
|
||||
ci->advance_buffer(data_offset);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
action = ci->get_command(¶m);
|
||||
|
||||
|
|
@ -198,34 +216,69 @@ enum codec_status codec_run(void)
|
|||
break;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* Do not allow seeking beyond the file's length */
|
||||
if ((unsigned) param > ci->id3->length) {
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
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(rmctx.data_offset + DATA_HEADER_SIZE +
|
||||
packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE));
|
||||
ci->seek_buffer(data_offset + packet_offset * packet_size);
|
||||
rmctx.audio_pkt_cnt = packet_offset;
|
||||
samplesdone = (rmctx.sample_rate/1000 * param);
|
||||
ci->set_elapsed(samplesdone/(frequency/1000));
|
||||
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;
|
||||
|
||||
filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE);
|
||||
consumed = rm_get_packet(&filebuf, &rmctx, &pkt);
|
||||
|
||||
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;
|
||||
if (rmctx.flags & RM_RAW_DATASTREAM) {
|
||||
if (n > consumed) {
|
||||
consumed += a52_decode_data(filebuf + consumed, filebuf + n);
|
||||
ci->set_offset(ci->curpos + consumed);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
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);
|
||||
|
||||
playback_on = 1;
|
||||
a52_decode_data(filebuf, filebuf + rmctx.block_align);
|
||||
ci->advance_buffer(pkt.length);
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
if (rm_get_packet(&filebuf, &rmctx, &pkt) < 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 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
playback_on = 1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "logf.h"
|
||||
#include "codeclib.h"
|
||||
#include "inttypes.h"
|
||||
#include "libatrac/atrac3.h"
|
||||
|
|
@ -37,10 +36,26 @@ static void init_rm(RMContext *rmctx)
|
|||
/* initialize the 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));
|
||||
}
|
||||
|
||||
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 */
|
||||
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 */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
static size_t buff_size;
|
||||
int datasize, res, consumed, i, time_offset;
|
||||
uint8_t *bit_buffer;
|
||||
uint16_t fs,sps,h;
|
||||
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;
|
||||
size_t resume_offset;
|
||||
intptr_t param;
|
||||
|
|
@ -85,14 +98,17 @@ enum codec_status codec_run(void)
|
|||
ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ?
|
||||
STEREO_MONO : STEREO_NONINTERLEAVED);
|
||||
|
||||
packet_header_size = PACKET_HEADER_SIZE +
|
||||
((rmctx.flags & RM_PKT_V1) ? 1 : 0);
|
||||
packet_count = rmctx.nb_packets;
|
||||
rmctx.audio_framesize = rmctx.block_align;
|
||||
rmctx.block_align = rmctx.sub_packet_size;
|
||||
fs = rmctx.audio_framesize;
|
||||
sps= rmctx.block_align;
|
||||
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);
|
||||
if(res < 0) {
|
||||
DEBUGF("failed to initialize RM atrac decoder\n");
|
||||
|
|
@ -102,10 +118,10 @@ enum codec_status codec_run(void)
|
|||
/* check for a mid-track resume and force a seek time accordingly */
|
||||
if(resume_offset) {
|
||||
resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
num_units = (int)resume_offset / scrambling_unit_size;
|
||||
/* put number of subpackets to skip in resume_offset */
|
||||
resume_offset /= (sps + PACKET_HEADER_SIZE);
|
||||
elapsed = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
|
||||
num_units = (int)resume_offset / scrambling_unit_size;
|
||||
/* put number of packets to skip in resume_offset */
|
||||
resume_offset = num_units * h;
|
||||
elapsed = (int)resume_offset * ((8000LL * fs)/rmctx.bit_rate);
|
||||
}
|
||||
|
||||
if (elapsed > 0) {
|
||||
|
|
@ -123,8 +139,9 @@ enum codec_status codec_run(void)
|
|||
seek_start :
|
||||
while((unsigned)elapsed < rmctx.duration)
|
||||
{
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
consumed = request_packet(scrambling_unit_size);
|
||||
if (!consumed)
|
||||
break;
|
||||
if(consumed < 0 && playback_on != 0) {
|
||||
if(playback_on == -1) {
|
||||
/* Error only if packet-parsing failed and playback hadn't started */
|
||||
|
|
@ -135,7 +152,7 @@ seek_start :
|
|||
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)
|
||||
action = ci->get_command(¶m);
|
||||
|
|
@ -164,10 +181,11 @@ seek_start :
|
|||
action = CODEC_ACTION_NULL;
|
||||
goto seek_start;
|
||||
}
|
||||
num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps));
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units);
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
num_units = (param/(sps*1000*8/rmctx.bit_rate))/spn;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
|
||||
consumed = request_packet(scrambling_unit_size);
|
||||
if (!consumed)
|
||||
return CODEC_OK;
|
||||
if(consumed < 0 && playback_on != 0) {
|
||||
if(playback_on == -1) {
|
||||
/* Error only if packet-parsing failed and playback hadn't started */
|
||||
|
|
@ -178,19 +196,32 @@ seek_start :
|
|||
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));
|
||||
while(rmctx.audiotimestamp > (unsigned) param) {
|
||||
while (rmctx.audiotimestamp > (unsigned)param && num_units-- > 0) {
|
||||
rmctx.audio_pkt_cnt = 0;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1));
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
packet_count += rmctx.audio_pkt_cnt;
|
||||
num_units--;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
|
||||
consumed = request_packet(scrambling_unit_size);
|
||||
if (!consumed)
|
||||
return CODEC_OK;
|
||||
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;
|
||||
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->seek_complete();
|
||||
}
|
||||
|
|
@ -198,11 +229,11 @@ seek_start :
|
|||
action = CODEC_ACTION_NULL;
|
||||
|
||||
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 */
|
||||
return CODEC_OK;
|
||||
|
||||
if(res != rmctx.block_align) {
|
||||
if (res != sps) {
|
||||
DEBUGF("codec error\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
|
@ -214,9 +245,9 @@ seek_start :
|
|||
ci->set_elapsed(elapsed);
|
||||
rmctx.frame_number++;
|
||||
}
|
||||
packet_count -= rmctx.audio_pkt_cnt;
|
||||
packet_count -= h;
|
||||
rmctx.audio_pkt_cnt = 0;
|
||||
ci->advance_buffer(consumed);
|
||||
ci->advance_buffer(scrambling_unit_size);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "logf.h"
|
||||
#include "codeclib.h"
|
||||
#include "inttypes.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));
|
||||
}
|
||||
|
||||
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 */
|
||||
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 */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
static size_t buff_size;
|
||||
int datasize, res, consumed, i, time_offset;
|
||||
uint8_t *bit_buffer;
|
||||
uint16_t fs,sps,h;
|
||||
uint32_t packet_count;
|
||||
int scrambling_unit_size, num_units;
|
||||
int spn, packet_header_size, scrambling_unit_size, num_units;
|
||||
size_t resume_offset;
|
||||
intptr_t param;
|
||||
long action;
|
||||
|
|
@ -84,14 +97,17 @@ enum codec_status codec_run(void)
|
|||
ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ?
|
||||
STEREO_MONO : STEREO_NONINTERLEAVED);
|
||||
|
||||
packet_header_size = PACKET_HEADER_SIZE +
|
||||
((rmctx.flags & RM_PKT_V1) ? 1 : 0);
|
||||
packet_count = rmctx.nb_packets;
|
||||
rmctx.audio_framesize = rmctx.block_align;
|
||||
rmctx.block_align = rmctx.sub_packet_size;
|
||||
fs = rmctx.audio_framesize;
|
||||
sps= rmctx.block_align;
|
||||
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);
|
||||
if(res < 0) {
|
||||
DEBUGF("failed to initialize cook decoder\n");
|
||||
|
|
@ -101,10 +117,10 @@ enum codec_status codec_run(void)
|
|||
/* check for a mid-track resume and force a seek time accordingly */
|
||||
if(resume_offset) {
|
||||
resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
num_units = (int)resume_offset / scrambling_unit_size;
|
||||
/* put number of subpackets to skip in resume_offset */
|
||||
resume_offset /= (sps + PACKET_HEADER_SIZE);
|
||||
param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
|
||||
num_units = (int)resume_offset / scrambling_unit_size;
|
||||
/* put number of packets to skip in resume_offset */
|
||||
resume_offset = num_units * h;
|
||||
param = (int)resume_offset * ((8000LL * fs)/rmctx.bit_rate);
|
||||
}
|
||||
|
||||
if (param) {
|
||||
|
|
@ -120,14 +136,15 @@ enum codec_status codec_run(void)
|
|||
seek_start :
|
||||
while(packet_count)
|
||||
{
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
consumed = request_packet(scrambling_unit_size);
|
||||
if (!consumed)
|
||||
break;
|
||||
if(consumed < 0) {
|
||||
DEBUGF("rm_get_packet failed\n");
|
||||
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)
|
||||
action = ci->get_command(¶m);
|
||||
|
|
@ -155,52 +172,65 @@ seek_start :
|
|||
action = CODEC_ACTION_NULL;
|
||||
goto seek_start;
|
||||
}
|
||||
num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps));
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units);
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
num_units = (param/(sps*1000*8/rmctx.bit_rate))/spn;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
|
||||
consumed = request_packet(scrambling_unit_size);
|
||||
if (!consumed) {
|
||||
ci->seek_complete();
|
||||
return CODEC_OK;
|
||||
}
|
||||
if(consumed < 0) {
|
||||
DEBUGF("rm_get_packet failed\n");
|
||||
DEBUGF("rm_get_packet failed\n");
|
||||
ci->seek_complete();
|
||||
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));
|
||||
while(rmctx.audiotimestamp > (unsigned) param) {
|
||||
while(rmctx.audiotimestamp > (unsigned)param && num_units-- > 0) {
|
||||
rmctx.audio_pkt_cnt = 0;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1));
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
packet_count += rmctx.audio_pkt_cnt;
|
||||
num_units--;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + scrambling_unit_size * num_units);
|
||||
consumed = request_packet(scrambling_unit_size);
|
||||
if (!consumed) {
|
||||
ci->seek_complete();
|
||||
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;
|
||||
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();
|
||||
}
|
||||
|
||||
action = CODEC_ACTION_NULL;
|
||||
|
||||
res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align);
|
||||
rmctx.frame_number++;
|
||||
res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], sps);
|
||||
|
||||
/* skip the first two frames; no valid audio */
|
||||
if(rmctx.frame_number < 3) continue;
|
||||
|
||||
if(res != rmctx.block_align) {
|
||||
if (res != sps) {
|
||||
DEBUGF("codec error\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(rm_outbuf,
|
||||
rm_outbuf+q.samples_per_channel,
|
||||
q.samples_per_channel);
|
||||
if(datasize)
|
||||
ci->pcmbuf_insert(rm_outbuf,
|
||||
rm_outbuf+q.samples_per_channel,
|
||||
q.samples_per_channel);
|
||||
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;
|
||||
ci->advance_buffer(consumed);
|
||||
ci->advance_buffer(scrambling_unit_size);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@
|
|||
#include "codeclib.h"
|
||||
#endif
|
||||
|
||||
#define SWAP(a, b) do{uint8_t SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
|
||||
|
||||
#ifdef TEST
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -500,17 +498,33 @@ void rm_get_packet_fd(int fd,RMContext *rmctx, RMPacket *pkt)
|
|||
}
|
||||
#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 consumed = 0;
|
||||
int headerlen;
|
||||
/* rockbox: comment 'set but unused' variables
|
||||
uint8_t unknown;
|
||||
*/
|
||||
uint16_t x, place;
|
||||
uint16_t x;
|
||||
uint16_t sps = rmctx->sub_packet_size;
|
||||
uint16_t h = rmctx->sub_packet_h;
|
||||
uint16_t y = rmctx->sub_packet_cnt;
|
||||
uint16_t y = 0;
|
||||
uint16_t w = rmctx->audio_framesize;
|
||||
|
||||
rmctx->sub_packet_cnt = 0;
|
||||
rmctx->audio_pkt_cnt = 0;
|
||||
|
||||
do
|
||||
{
|
||||
y = rmctx->sub_packet_cnt;
|
||||
|
|
@ -523,6 +537,7 @@ int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt)
|
|||
return -1;
|
||||
}
|
||||
|
||||
headerlen = PACKET_HEADER_SIZE + (pkt->version ? 1 : 0);
|
||||
pkt->length = rm_get_uint16be(*src+2);
|
||||
pkt->stream_number = rm_get_uint16be(*src+4);
|
||||
pkt->timestamp = rm_get_uint32be(*src+6);
|
||||
|
|
@ -534,25 +549,27 @@ int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt)
|
|||
pkt->flags = rm_get_uint8(*src+11);
|
||||
|
||||
if(pkt->version == 1)
|
||||
/* unknown = */ rm_get_uint8(*src+10);
|
||||
/* unknown = */ rm_get_uint8(*src+12);
|
||||
|
||||
if (pkt->flags & 2) /* keyframe */
|
||||
y = rmctx->sub_packet_cnt = 0;
|
||||
if (pkt->flags & 2) { /* keyframe */
|
||||
if (y)
|
||||
return consumed;
|
||||
y = 0;
|
||||
}
|
||||
if (!y)
|
||||
rmctx->audiotimestamp = pkt->timestamp;
|
||||
|
||||
|
||||
/* Skip packet header */
|
||||
advance_buffer(src, PACKET_HEADER_SIZE);
|
||||
consumed += PACKET_HEADER_SIZE;
|
||||
advance_buffer(src, headerlen);
|
||||
consumed += headerlen;
|
||||
if (rmctx->codec_type == CODEC_COOK || rmctx->codec_type == CODEC_ATRAC) {
|
||||
for(x = 0 ; x < w/sps; x++)
|
||||
{
|
||||
place = sps*(h*x+((h+1)/2)*(y&1)+(y>>1));
|
||||
pkt->frames[place/sps] = *src;
|
||||
pkt->frames[h*x+((h+1)/2)*(y&1)+(y>>1)] = *src;
|
||||
advance_buffer(src,sps);
|
||||
consumed += sps;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rmctx->codec_type == CODEC_AAC) {
|
||||
rmctx->sub_packet_cnt = (rm_get_uint16be(*src) & 0xf0) >> 4;
|
||||
advance_buffer(src, 2);
|
||||
|
|
@ -563,22 +580,22 @@ int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt)
|
|||
advance_buffer(src, 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) {
|
||||
/* The byte order of the data is reversed from standard AC3 */
|
||||
for(x = 0; x < pkt->length - PACKET_HEADER_SIZE; x+=2) {
|
||||
SWAP((*src)[0], (*src)[1]);
|
||||
*src += 2;
|
||||
}
|
||||
*src -= x;
|
||||
rm_ac3_swap_bytes(*src, pkt->length - headerlen);
|
||||
break;
|
||||
}
|
||||
else return -1; /* invalid codec type */
|
||||
|
||||
rmctx->audio_pkt_cnt++;
|
||||
}while(++(rmctx->sub_packet_cnt) < h);
|
||||
|
||||
return consumed;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
@ -587,6 +604,6 @@ void dump_rm_context(RMContext *rmctx)
|
|||
DEBUGF("block_align = %d\n", rmctx->block_align);
|
||||
DEBUGF("nb_channels = %d\n", rmctx->nb_channels);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
#include <inttypes.h>
|
||||
#include "bytestream.h"
|
||||
|
||||
#define RM_RAW_DATASTREAM 0x0100
|
||||
#define RM_PKT_V1 0x0200
|
||||
|
||||
#define MAX_EXTRADATA_SIZE 16
|
||||
#define DATA_HEADER_SIZE 18
|
||||
#define PACKET_HEADER_SIZE 12
|
||||
|
|
@ -86,6 +89,8 @@ typedef struct rm_context
|
|||
|
||||
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 */
|
||||
int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt);
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,8 @@ enum codec_status codec_run(void)
|
|||
if (resume_offset) {
|
||||
resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ static char* fourcc2str(uint32_t f)
|
|||
}
|
||||
#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;
|
||||
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));
|
||||
if (((version >> 16) & 0xff) == 3) {
|
||||
/* Very old version */
|
||||
return -1;
|
||||
} else {
|
||||
#ifdef SIMULATOR
|
||||
real_read_object_header(fd, &obj);
|
||||
|
|
@ -218,7 +219,7 @@ static inline int real_read_audio_stream_info(int fd, RMContext *rmctx)
|
|||
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;
|
||||
int res;
|
||||
|
|
@ -242,6 +243,7 @@ static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3)
|
|||
uint32_t max_bitrate;
|
||||
uint16_t num_streams;
|
||||
uint32_t next_data_off;
|
||||
uint16_t pkt_version;
|
||||
uint8_t header_end;
|
||||
|
||||
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))
|
||||
{
|
||||
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 */
|
||||
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(" num_streams = %d\n",num_streams);
|
||||
DEBUGF(" flags=0x%04x\n",rmctx->flags);
|
||||
|
||||
rmctx->flags &= 0x00FF;
|
||||
break;
|
||||
|
||||
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(" next DATA offset = %ld\n",next_data_off);
|
||||
header_end = 1;
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(header_end) break;
|
||||
|
|
@ -456,7 +504,7 @@ bool get_rm_metadata(int fd, struct mp3entry* id3)
|
|||
|
||||
id3->channels = rmctx->nb_channels;
|
||||
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->length = rmctx->duration;
|
||||
id3->filesize = filesize(fd);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue