diff options
| author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-24 01:25:21 -0400 |
|---|---|---|
| committer | Nils Wallménius <nils@rockbox.org> | 2012-03-18 12:00:39 +0100 |
| commit | b5716df4cb2837bbbc42195cf1aefcf03e21d6a6 (patch) | |
| tree | 130cd712e2e00893b6df9959a375a8d9523a1aca /lib/rbcodec/metadata/rm.c | |
| parent | 24bd9d5393dbe39a5c6194877bc00ede669b1d5d (diff) | |
| download | rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.zip rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.tar.gz rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.tar.bz2 rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.tar.xz | |
Build librbcodec with DSP and metadata.
All associated files are moved to /lib/rbcodec.
Change-Id: I572ddd2b8a996aae1e98c081d06b1ed356dce222
Diffstat (limited to 'lib/rbcodec/metadata/rm.c')
| -rw-r--r-- | lib/rbcodec/metadata/rm.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/lib/rbcodec/metadata/rm.c b/lib/rbcodec/metadata/rm.c new file mode 100644 index 0000000..27f541c --- /dev/null +++ b/lib/rbcodec/metadata/rm.c @@ -0,0 +1,464 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <inttypes.h> + +#include <codecs/librm/rm.h> +#include "system.h" +#include "metadata.h" +#include "metadata_common.h" +#include "metadata_parsers.h" +#include "logf.h" + +/* Uncomment the following line for debugging */ +//#define DEBUG_RM +#ifndef DEBUG_RM +#undef DEBUGF +#define DEBUGF(...) +#endif + +#define ID3V1_OFFSET -128 +#define METADATA_FOOTER_OFFSET -140 + +static inline void print_cook_extradata(RMContext *rmctx) { + + DEBUGF(" cook_version = 0x%08lx\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; +}; + +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; +} + +#if (defined(SIMULATOR) && defined(DEBUG_RM)) +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; +} +#endif + +static inline int real_read_audio_stream_info(int fd, RMContext *rmctx) +{ + int skipped = 0; + uint32_t version; + struct real_object_t obj; +#ifdef SIMULATOR + uint32_t header_size; + uint16_t flavor; + uint32_t coded_framesize; + uint8_t interleaver_id_length; + uint8_t fourcc_length; +#endif + uint32_t interleaver_id; + uint32_t fourcc = 0; + + memset(&obj,0,sizeof(obj)); + read_uint32be(fd, &version); + skipped += 4; + + DEBUGF(" version=0x%04lx\n",((version >> 16) & 0xff)); + if (((version >> 16) & 0xff) == 3) { + /* Very old version */ + } else { +#ifdef SIMULATOR + real_read_object_header(fd, &obj); + read_uint32be(fd, &header_size); + /* obj.size will be filled with an unknown value, replaced with header_size */ + DEBUGF(" Object: %s, size: %ld bytes, version: 0x%04x\n",fourcc2str(obj.fourcc),header_size,obj.version); + + read_uint16be(fd, &flavor); + read_uint32be(fd, &coded_framesize); +#else + lseek(fd, 20, SEEK_CUR); +#endif + lseek(fd, 12, SEEK_CUR); /* unknown */ + read_uint16be(fd, &rmctx->sub_packet_h); + read_uint16be(fd, &rmctx->block_align); + read_uint16be(fd, &rmctx->sub_packet_size); + lseek(fd, 2, SEEK_CUR); /* unknown */ + skipped += 40; + if (((version >> 16) & 0xff) == 5) + { + lseek(fd, 6, SEEK_CUR); /* unknown */ + skipped += 6; + } + read_uint16be(fd, &rmctx->sample_rate); + lseek(fd, 4, SEEK_CUR); /* unknown */ + read_uint16be(fd, &rmctx->nb_channels); + skipped += 8; + if (((version >> 16) & 0xff) == 4) + { +#ifdef SIMULATOR + read_uint8(fd, &interleaver_id_length); + read_uint32be(fd, &interleaver_id); + read_uint8(fd, &fourcc_length); +#else + lseek(fd, 6, SEEK_CUR); +#endif + read_uint32be(fd, &fourcc); + skipped += 10; + } + if (((version >> 16) & 0xff) == 5) + { + read_uint32be(fd, &interleaver_id); + read_uint32be(fd, &fourcc); + skipped += 8; + } + lseek(fd, 3, SEEK_CUR); /* unknown */ + skipped += 3; + if (((version >> 16) & 0xff) == 5) + { + lseek(fd, 1, SEEK_CUR); /* unknown */ + 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('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; + + 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; + + default: /* Not a supported codec */ + return -1; + } + + DEBUGF(" flavor = %d\n",flavor); + DEBUGF(" coded_frame_size = %ld\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 = %ld\n",rmctx->extradata_size); + DEBUGF(" codec_extradata :\n"); + if(rmctx->codec_type == CODEC_COOK) { + DEBUGF(" cook_extradata :\n"); + print_cook_extradata(rmctx); + } + + } + + return skipped; +} + +static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3) +{ + struct real_object_t obj; + int res; + int skipped; + off_t curpos __attribute__((unused)); + uint8_t len; /* Holds a string_length, which is then passed to read_string() */ + +#ifdef SIMULATOR + 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 stream_id; + uint32_t start_time; + uint32_t codec_data_size; +#endif + uint32_t v; + uint32_t max_bitrate; + uint16_t num_streams; + uint32_t next_data_off; + uint8_t header_end; + + memset(&obj,0,sizeof(obj)); + 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; + } + + lseek(fd, 8, SEEK_CUR); /* unknown */ + + DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); + + 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*/ +#ifdef SIMULATOR + read_uint32be(fd, &max_packet_size); + read_uint32be(fd, &avg_packet_size); + read_uint32be(fd, &packet_count); +#else + lseek(fd, 3*sizeof(uint32_t), SEEK_CUR); +#endif + read_uint32be(fd, &rmctx->duration); +#ifdef SIMULATOR + read_uint32be(fd, &preroll); + read_uint32be(fd, &index_offset); +#else + lseek(fd, 2*sizeof(uint32_t), SEEK_CUR); +#endif + read_uint32be(fd, &rmctx->data_offset); + read_uint16be(fd, &num_streams); + read_uint16be(fd, &rmctx->flags); + skipped += 40; + + DEBUGF(" max_bitrate = %ld\n",max_bitrate); + DEBUGF(" avg_bitrate = %ld\n",rmctx->bit_rate); + DEBUGF(" max_packet_size = %ld\n",max_packet_size); + DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); + DEBUGF(" packet_count = %ld\n",packet_count); + DEBUGF(" duration = %ld\n",rmctx->duration); + DEBUGF(" preroll = %ld\n",preroll); + DEBUGF(" index_offset = %ld\n",index_offset); + DEBUGF(" data_offset = %ld\n",rmctx->data_offset); + DEBUGF(" num_streams = %d\n",num_streams); + DEBUGF(" flags=0x%04x\n",rmctx->flags); + break; + + case FOURCC('C','O','N','T'): + /* Four strings - Title, Author, Copyright, Comment */ + 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); + skipped += 4; + + 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]); + break; + + case FOURCC('M','D','P','R'): /* Media properties */ +#ifdef SIMULATOR + read_uint16be(fd,&stream_id); + read_uint32be(fd,&max_bitrate); + read_uint32be(fd,&avg_bitrate); + read_uint32be(fd,&max_packet_size); + read_uint32be(fd,&avg_packet_size); + read_uint32be(fd,&start_time); + read_uint32be(fd,&preroll); + read_uint32be(fd,&duration); +#else + lseek(fd, 30, SEEK_CUR); +#endif + skipped += 30; + read_uint8(fd,&len); + skipped += 1; + lseek(fd, len, SEEK_CUR); /* desc */ + skipped += len; + read_uint8(fd,&len); + skipped += 1; +#ifdef SIMULATOR + lseek(fd, len, SEEK_CUR); /* mimetype */ + read_uint32be(fd,&codec_data_size); +#else + lseek(fd, len + 4, SEEK_CUR); +#endif + skipped += len + 4; + read_uint32be(fd,&v); + skipped += 4; + + DEBUGF(" stream_id = 0x%04x\n",stream_id); + DEBUGF(" max_bitrate = %ld\n",max_bitrate); + DEBUGF(" avg_bitrate = %ld\n",avg_bitrate); + DEBUGF(" max_packet_size = %ld\n",max_packet_size); + DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); + DEBUGF(" start_time = %ld\n",start_time); + DEBUGF(" preroll = %ld\n",preroll); + DEBUGF(" duration = %ld\n",duration); + DEBUGF(" codec_data_size = %ld\n",codec_data_size); + DEBUGF(" v=\"%s\"\n", fourcc2str(v)); + + if (v == FOURCC('.','r','a',0xfd)) + { + int temp; + temp= real_read_audio_stream_info(fd, rmctx); + if(temp < 0) + return -1; + else + skipped += temp; + } + else if (v == FOURCC('L','S','D',':')) + { + DEBUGF("Real audio lossless is not supported."); + return -1; + } + else + { + /* We shall not abort with -1 here. *.rm file often seem + * to have a second media properties header that contains + * other metadata. */ + DEBUGF("Unknown header signature :\"%s\"\n", fourcc2str(v)); + } + + + break; + + case FOURCC('D','A','T','A'): + read_uint32be(fd,&rmctx->nb_packets); + skipped += 4; + read_uint32be(fd,&next_data_off); + skipped += 4; + + /*** + * 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 = %ld\n",rmctx->nb_packets); + DEBUGF(" next DATA offset = %ld\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; +} + + +bool get_rm_metadata(int fd, struct mp3entry* id3) +{ + RMContext *rmctx = (RMContext*) (( (intptr_t)id3->id3v2buf + 3 ) &~ 3); + memset(rmctx,0,sizeof(RMContext)); + if(rm_parse_header(fd, rmctx, id3) < 0) + return false; + + if (!setid3v1title(fd, id3)) { + /* file has no id3v1 tags, use the tags from CONT chunk */ + id3->title = id3->id3v1buf[0]; + id3->artist = id3->id3v1buf[1]; + id3->comment= id3->id3v1buf[3]; + } + + switch(rmctx->codec_type) + { + case CODEC_COOK: + /* Already set, do nothing */ + break; + case CODEC_AAC: + id3->codectype = AFMT_RM_AAC; + break; + + case CODEC_AC3: + id3->codectype = AFMT_RM_AC3; + break; + + case CODEC_ATRAC: + id3->codectype = AFMT_RM_ATRAC3; + break; + } + + id3->channels = rmctx->nb_channels; + id3->extradata_size = rmctx->extradata_size; + id3->bitrate = rmctx->bit_rate / 1000; + id3->frequency = rmctx->sample_rate; + id3->length = rmctx->duration; + id3->filesize = filesize(fd); + return true; +} |