From f40bfc9267b13b54e6379dfe7539447662879d24 Mon Sep 17 00:00:00 2001 From: Sean Bartell Date: Sat, 25 Jun 2011 21:32:25 -0400 Subject: Add codecs to librbcodec. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97 Reviewed-on: http://gerrit.rockbox.org/137 Reviewed-by: Nils Wallménius Tested-by: Nils Wallménius --- lib/rbcodec/codecs/librm/rm.c | 592 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 592 insertions(+) create mode 100644 lib/rbcodec/codecs/librm/rm.c (limited to 'lib/rbcodec/codecs/librm/rm.c') diff --git a/lib/rbcodec/codecs/librm/rm.c b/lib/rbcodec/codecs/librm/rm.c new file mode 100644 index 0000000000..eabbe5d92a --- /dev/null +++ b/lib/rbcodec/codecs/librm/rm.c @@ -0,0 +1,592 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 Dave Chapman + * Copyright (C) 2009 Mohamed Tarek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include + +#include "rm.h" +#ifdef ROCKBOX +#include "codeclib.h" +#endif + +#define SWAP(a, b) do{uint8_t SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) + +#ifdef TEST +#include +#include +#include "inttypes.h" +#include + +int filesize(int fd) +{ + struct stat buf; + + if (fstat(fd,&buf) == -1) { + return -1; + } else { + return (int)buf.st_size; + } +} + +static int read_uint8(int fd, uint8_t* buf) +{ + unsigned char tmp[1]; + int res; + + res=read(fd, tmp, 1); + *buf = tmp[0]; + return res; +} + +static int read_uint16be(int fd, uint16_t* buf) +{ + unsigned char tmp[2]; + int res; + + res=read(fd, tmp, 2); + *buf = (tmp[0] << 8) | tmp[1]; + return res; +} + +static int read_uint32be(int fd, uint32_t* buf) +{ + unsigned char tmp[4]; + int res; + + res=read(fd, tmp, 4); + *buf = (tmp[0] << 24) | (tmp[1] << 16) | (tmp[2] << 8) | tmp[3]; + return res; +} + + +static void print_cook_extradata(RMContext *rmctx) { + + DEBUGF(" cook_version = 0x%08x\n", rm_get_uint32be(rmctx->codec_extradata)); + DEBUGF(" samples_per_frame_per_channel = %d\n", rm_get_uint16be(&rmctx->codec_extradata[4])); + DEBUGF(" number_of_subbands_in_freq_domain = %d\n", rm_get_uint16be(&rmctx->codec_extradata[6])); + if(rmctx->extradata_size == 16) { + DEBUGF(" joint_stereo_subband_start = %d\n",rm_get_uint16be(&rmctx->codec_extradata[12])); + DEBUGF(" joint_stereo_vlc_bits = %d\n", rm_get_uint16be(&rmctx->codec_extradata[14])); + } +} + + +struct real_object_t +{ + uint32_t fourcc; + uint32_t size; + uint16_t version; +}; + +#define FOURCC(a,b,c,d) (((a)<<24) | ((b) << 16) | ((c) << 8) | (d)) + +static int real_read_object_header(int fd, struct real_object_t* obj) +{ + int n; + + if ((n = read_uint32be(fd, &obj->fourcc)) <= 0) return n; + if ((n = read_uint32be(fd, &obj->size)) <= 0) return n; + if ((n = read_uint16be(fd, &obj->version)) <= 0) return n; + + return 1; +} + +static char* fourcc2str(uint32_t f) +{ + static char res[5]; + + res[0] = (f & 0xff000000) >> 24; + res[1] = (f & 0xff0000) >> 16; + res[2] = (f & 0xff00) >> 8; + res[3] = (f & 0xff); + res[4] = 0; + + return res; +} + +static int read_str(int fd, char* buf) +{ + uint8_t len; + int res; + + res = read(fd, &len, 1); + res = read(fd, buf, len); + buf[len]=0; + + return len+1; +} + +static int real_read_audio_stream_info(int fd, RMContext *rmctx) +{ + int skipped = 0; + uint32_t version; + struct real_object_t obj; + memset(&obj,0,sizeof(obj)); + uint32_t header_size; + uint16_t flavor; + uint32_t coded_framesize; + uint32_t unknown1; + uint32_t unknown2; + uint32_t unknown3; + uint16_t unknown4; + uint16_t unknown5; + uint16_t unknown6; + uint16_t unknown7; + uint32_t unknown8; + uint8_t interleaver_id_length; + uint32_t interleaver_id; + uint8_t fourcc_length; + uint32_t fourcc = 0; + uint8_t unknown9; + uint16_t unknown10; + uint8_t unknown11; + + read_uint32be(fd, &version); + skipped += 4; + + DEBUGF(" version=0x%04x\n",((version >> 16) & 0xff)); + if (((version >> 16) & 0xff) == 3) { + /* Very old version */ + } else { + real_read_object_header(fd, &obj); + skipped += 10; + read_uint32be(fd, &header_size); + skipped += 4; + /* obj.size will be filled with an unknown value, replaced with header_size */ + DEBUGF(" Object: %s, size: %d bytes, version: 0x%04x\n",fourcc2str(obj.fourcc),header_size,obj.version); + + read_uint16be(fd, &flavor); + read_uint32be(fd, &coded_framesize); + read_uint32be(fd, &unknown1); + read_uint32be(fd, &unknown2); + read_uint32be(fd, &unknown3); + read_uint16be(fd, &rmctx->sub_packet_h); + read_uint16be(fd, &rmctx->block_align); + read_uint16be(fd, &rmctx->sub_packet_size); + read_uint16be(fd, &unknown4); + skipped += 26; + if (((version >> 16) & 0xff) == 5) + { + read_uint16be(fd, &unknown5); + read_uint16be(fd, &unknown6); + read_uint16be(fd, &unknown7); + skipped += 6; + } + read_uint16be(fd, &rmctx->sample_rate); + read_uint32be(fd, &unknown8); + read_uint16be(fd, &rmctx->nb_channels); + skipped += 8; + if (((version >> 16) & 0xff) == 4) + { + read_uint8(fd, &interleaver_id_length); + read_uint32be(fd, &interleaver_id); + read_uint8(fd, &fourcc_length); + read_uint32be(fd, &fourcc); + skipped += 10; + } + if (((version >> 16) & 0xff) == 5) + { + read_uint32be(fd, &interleaver_id); + read_uint32be(fd, &fourcc); + skipped += 8; + } + read_uint8(fd,&unknown9); + read_uint16be(fd,&unknown10); + skipped += 3; + if (((version >> 16) & 0xff) == 5) + { + read_uint8(fd, &unknown11); + skipped += 1; + } + + switch(fourcc) { + case FOURCC('c','o','o','k'): + rmctx->codec_type = CODEC_COOK; + read_uint32be(fd, &rmctx->extradata_size); + skipped += 4; + read(fd, rmctx->codec_extradata, rmctx->extradata_size); + skipped += rmctx->extradata_size; + break; + + case FOURCC('a','t','r','c'): + rmctx->codec_type = CODEC_ATRAC; + read_uint32be(fd, &rmctx->extradata_size); + skipped += 4; + read(fd, rmctx->codec_extradata, rmctx->extradata_size); + skipped += rmctx->extradata_size; + break; + + case FOURCC('r','a','a','c'): + case FOURCC('r','a','c','p'): + rmctx->codec_type = CODEC_AAC; + read_uint32be(fd, &rmctx->extradata_size); + skipped += 4; + read(fd, rmctx->codec_extradata, rmctx->extradata_size); + skipped += rmctx->extradata_size; + break; + + case FOURCC('d','n','e','t'): + rmctx->codec_type = CODEC_AC3; + break; + + default: /* Not a supported codec */ + return -1; + } + + + DEBUGF(" flavor = %d\n",flavor); + DEBUGF(" coded_frame_size = %d\n",coded_framesize); + DEBUGF(" sub_packet_h = %d\n",rmctx->sub_packet_h); + DEBUGF(" frame_size = %d\n",rmctx->block_align); + DEBUGF(" sub_packet_size = %d\n",rmctx->sub_packet_size); + DEBUGF(" sample_rate= %d\n",rmctx->sample_rate); + DEBUGF(" channels= %d\n",rmctx->nb_channels); + DEBUGF(" fourcc = %s\n",fourcc2str(fourcc)); + DEBUGF(" codec_extra_data_length = %d\n",rmctx->extradata_size); + if(rmctx->codec_type == CODEC_COOK) { + DEBUGF(" cook_extradata :\n"); + print_cook_extradata(rmctx); + } + + } + + return skipped; +} + +int real_parse_header(int fd, RMContext *rmctx) +{ + struct real_object_t obj; + memset(&obj,0,sizeof(obj)); + int res; + int skipped; + off_t curpos; + + uint32_t unknown1; + uint32_t unknown2; + + uint32_t max_bitrate; + uint32_t avg_bitrate = 0; + uint32_t max_packet_size; + uint32_t avg_packet_size; + uint32_t packet_count; + uint32_t duration; + uint32_t preroll; + uint32_t index_offset; + uint16_t num_streams; + uint16_t flags = 0; + + uint16_t stream_id; + uint32_t start_time; + char desc[256]; + char mimetype[256]; + uint32_t codec_data_size; + uint32_t v; + + char title[256]; + char author[256]; + char copyright[256]; + char comment[256]; + + uint32_t next_data_off; + uint8_t header_end; + + curpos = lseek(fd, 0, SEEK_SET); + res = real_read_object_header(fd, &obj); + + if (obj.fourcc == FOURCC('.','r','a',0xfd)) + { + /* Very old .ra format - not yet supported */ + return -1; + } + else if (obj.fourcc != FOURCC('.','R','M','F')) + { + return -1; + } + + read_uint32be(fd, &unknown1); + read_uint32be(fd, &unknown2); + + DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); + DEBUGF(" unknown1=%d (0x%08x)\n",unknown1,unknown1); + DEBUGF(" unknown2=%d (0x%08x)\n",unknown2,unknown2); + + res = real_read_object_header(fd, &obj); + header_end = 0; + while(res) + { + DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); + skipped = 10; + if(obj.fourcc == FOURCC('I','N','D','X')) + break; + switch (obj.fourcc) + { + case FOURCC('P','R','O','P'): /* File properties */ + read_uint32be(fd, &max_bitrate); + read_uint32be(fd, &rmctx->bit_rate); /*avg bitrate*/ + read_uint32be(fd, &max_packet_size); + read_uint32be(fd, &avg_packet_size); + read_uint32be(fd, &packet_count); + read_uint32be(fd, &rmctx->duration); + read_uint32be(fd, &preroll); + read_uint32be(fd, &index_offset); + read_uint32be(fd, &rmctx->data_offset); + read_uint16be(fd, &num_streams); + read_uint16be(fd, &rmctx->flags); + skipped += 40; + + DEBUGF(" max_bitrate = %d\n",max_bitrate); + DEBUGF(" avg_bitrate = %d\n",avg_bitrate); + DEBUGF(" max_packet_size = %d\n",max_packet_size); + DEBUGF(" avg_packet_size = %d\n",avg_packet_size); + DEBUGF(" packet_count = %d\n",packet_count); + DEBUGF(" duration = %d\n",rmctx->duration); + DEBUGF(" preroll = %d\n",preroll); + DEBUGF(" index_offset = %d\n",index_offset); + DEBUGF(" data_offset = %d\n",rmctx->data_offset); + DEBUGF(" num_streams = %d\n",num_streams); + DEBUGF(" flags=0x%04x\n",flags); + break; + + case FOURCC('C','O','N','T'): + /* Four strings - Title, Author, Copyright, Comment */ + skipped += read_str(fd,title); + skipped += read_str(fd,author); + skipped += read_str(fd,copyright); + skipped += read_str(fd,comment); + + DEBUGF(" title=\"%s\"\n",title); + DEBUGF(" author=\"%s\"\n",author); + DEBUGF(" copyright=\"%s\"\n",copyright); + DEBUGF(" comment=\"%s\"\n",comment); + break; + + case FOURCC('M','D','P','R'): /* Media properties */ + read_uint16be(fd,&stream_id); + skipped += 2; + read_uint32be(fd,&max_bitrate); + skipped += 4; + read_uint32be(fd,&avg_bitrate); + skipped += 4; + read_uint32be(fd,&max_packet_size); + skipped += 4; + read_uint32be(fd,&avg_packet_size); + skipped += 4; + read_uint32be(fd,&start_time); + skipped += 4; + read_uint32be(fd,&preroll); + skipped += 4; + read_uint32be(fd,&duration); + skipped += 4; + skipped += read_str(fd,desc); + skipped += read_str(fd,mimetype); + read_uint32be(fd,&codec_data_size); + skipped += 4; + //From ffmpeg: codec_pos = url_ftell(pb); + read_uint32be(fd,&v); + skipped += 4; + + DEBUGF(" stream_id = 0x%04x\n",stream_id); + DEBUGF(" max_bitrate = %d\n",max_bitrate); + DEBUGF(" avg_bitrate = %d\n",avg_bitrate); + DEBUGF(" max_packet_size = %d\n",max_packet_size); + DEBUGF(" avg_packet_size = %d\n",avg_packet_size); + DEBUGF(" start_time = %d\n",start_time); + DEBUGF(" preroll = %d\n",preroll); + DEBUGF(" duration = %d\n",duration); + DEBUGF(" desc=\"%s\"\n",desc); + DEBUGF(" mimetype=\"%s\"\n",mimetype); + DEBUGF(" codec_data_size = %d\n",codec_data_size); + DEBUGF(" v=\"%s\"\n", fourcc2str(v)); + + if (v == FOURCC('.','r','a',0xfd)) + { + skipped += real_read_audio_stream_info(fd, rmctx); + } + + break; + + case FOURCC('D','A','T','A'): + + read_uint32be(fd,&rmctx->nb_packets); + skipped += 4; + read_uint32be(fd,&next_data_off); + skipped += 4; + if (!rmctx->nb_packets && (rmctx->flags & 4)) + rmctx->nb_packets = 3600 * 25; + + /*** + * nb_packets correction : + * in some samples, number of packets may not exactly form + * an integer number of scrambling units. This is corrected + * by constructing a partially filled unit out of the few + * remaining samples at the end of decoding. + ***/ + if(rmctx->nb_packets % rmctx->sub_packet_h) + rmctx->nb_packets += rmctx->sub_packet_h - (rmctx->nb_packets % rmctx->sub_packet_h); + + DEBUGF(" data_nb_packets = %d\n",rmctx->nb_packets); + DEBUGF(" next DATA offset = %d\n",next_data_off); + header_end = 1; + break; + } + if(header_end) break; + curpos = lseek(fd, obj.size - skipped, SEEK_CUR); + res = real_read_object_header(fd, &obj); + } + + + return 0; +} + +void rm_get_packet_fd(int fd,RMContext *rmctx, RMPacket *pkt) +{ + uint8_t unknown,packet_group; + 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 w = rmctx->audio_framesize; + int res; + do + { + y = rmctx->sub_packet_cnt; + read_uint16be(fd,&pkt->version); + read_uint16be(fd,&pkt->length); + read_uint16be(fd,&pkt->stream_number); + read_uint32be(fd,&pkt->timestamp); + DEBUGF(" version = %d\n" + " length = %d\n" + " stream = %d\n" + " timestmp= %d\n",pkt->version,pkt->length,pkt->stream_number,pkt->timestamp); + + if(pkt->version == 0) + { + read_uint8(fd,&packet_group); + read_uint8(fd,&pkt->flags); + } + if(pkt->version == 1) + read_uint8(fd,&unknown); + + if (pkt->flags & 2) /* keyframe */ + y = rmctx->sub_packet_cnt = 0; + if (!y) /* if keyframe update playback elapsed time */ + rmctx->audiotimestamp = pkt->timestamp; + + for(x = 0 ; x < w/sps; x++) + { + res = read(fd,pkt->data+(sps*(h*x+((h+1)/2)*(y&1)+(y>>1))), sps); + } + rmctx->audio_pkt_cnt++; + }while(++(rmctx->sub_packet_cnt) < h); + +} +#endif /*TEST*/ + +int rm_get_packet(uint8_t **src,RMContext *rmctx, RMPacket *pkt) +{ + int consumed = 0; + /* rockbox: comment 'set but unused' variables + uint8_t unknown; + */ + uint16_t x, place; + uint16_t sps = rmctx->sub_packet_size; + uint16_t h = rmctx->sub_packet_h; + uint16_t y = rmctx->sub_packet_cnt; + uint16_t w = rmctx->audio_framesize; + do + { + y = rmctx->sub_packet_cnt; + pkt->version = rm_get_uint16be(*src); + + /* Simple error checking */ + if(pkt->version != 0 && pkt->version != 1) + { + DEBUGF("parsing packets failed\n"); + return -1; + } + + pkt->length = rm_get_uint16be(*src+2); + pkt->stream_number = rm_get_uint16be(*src+4); + pkt->timestamp = rm_get_uint32be(*src+6); + /*DEBUGF(" version = %d\n" + " length = %d\n" + " stream = %d\n" + " timestamp= %d\n\n",pkt->version,pkt->length,pkt->stream_number,pkt->timestamp);*/ + /* unknown =*/ rm_get_uint8(*src+10); + pkt->flags = rm_get_uint8(*src+11); + + if(pkt->version == 1) + /* unknown = */ rm_get_uint8(*src+10); + + if (pkt->flags & 2) /* keyframe */ + y = rmctx->sub_packet_cnt = 0; + if (!y) + rmctx->audiotimestamp = pkt->timestamp; + + /* Skip packet header */ + advance_buffer(src, PACKET_HEADER_SIZE); + consumed += PACKET_HEADER_SIZE; + 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; + 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); + consumed += 2; + if (rmctx->sub_packet_cnt) { + for(x = 0; x < rmctx->sub_packet_cnt; x++) { + rmctx->sub_packet_lengths[x] = rm_get_uint16be(*src); + advance_buffer(src, 2); + consumed += 2; + } + rmctx->audio_pkt_cnt = --rmctx->sub_packet_cnt; + } + } + + 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; + } + rmctx->audio_pkt_cnt++; + }while(++(rmctx->sub_packet_cnt) < h); + +return consumed; +} + +#ifdef DEBUG +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 ); +} +#endif -- cgit v1.2.3