From 23ac6ffaa1efeea6417f7d064c0b8f4f280aa0cd Mon Sep 17 00:00:00 2001 From: Mohamed Tarek Date: Fri, 12 Feb 2010 03:21:37 +0000 Subject: Add support for Sony OMA file format. Currently only supports ATRAC3 (without DRM), and seeks. Tested on sansa e200. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24615 a1c6a512-1295-4272-9138-f99709370657 --- apps/SOURCES | 1 + apps/codecs/SOURCES | 1 + apps/codecs/atrac3_oma.c | 158 ++++++++++++++++++++++++++++++++ apps/codecs/codecs.make | 1 + apps/filetypes.c | 2 + apps/metadata.c | 10 +++ apps/metadata.h | 1 + apps/metadata/metadata_parsers.h | 1 + apps/metadata/oma.c | 188 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 363 insertions(+) create mode 100644 apps/codecs/atrac3_oma.c create mode 100644 apps/metadata/oma.c (limited to 'apps') diff --git a/apps/SOURCES b/apps/SOURCES index f99c2bc640..7580caabc9 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -181,6 +181,7 @@ metadata/a52.c metadata/asap.c metadata/rm.c metadata/nsf.c +metadata/oma.c #endif #ifdef HAVE_TAGCACHE tagcache.c diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES index 75d74ab33b..4c847c23e0 100644 --- a/apps/codecs/SOURCES +++ b/apps/codecs/SOURCES @@ -13,6 +13,7 @@ cook.c raac.c a52_rm.c atrac3_rm.c +atrac3_oma.c mpc.c wma.c sid.c diff --git a/apps/codecs/atrac3_oma.c b/apps/codecs/atrac3_oma.c new file mode 100644 index 0000000000..b080b71524 --- /dev/null +++ b/apps/codecs/atrac3_oma.c @@ -0,0 +1,158 @@ +/*************************************************************************** + * __________ __ ___. + * 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 + +#include "logf.h" +#include "codeclib.h" +#include "inttypes.h" +#include "libatrac/atrac3.h" + +CODEC_HEADER + +#define FRAMESIZE ci->id3->bytesperframe +#define BITRATE ci->id3->bitrate + +/* The codec has nothing to do with RM, it just uses an RMContext struct to * + * store the data needs to be passed to the decoder initializing function. */ +RMContext rmctx; +ATRAC3Context q IBSS_ATTR; + +static void init_rm(RMContext *rmctx, struct mp3entry *id3) +{ + rmctx->sample_rate = id3->frequency; + rmctx->nb_channels = 2; + rmctx->bit_rate = id3->bitrate; + rmctx->block_align = id3->bytesperframe; + + /* 14-byte extra-data was faked in the metadata parser so that * + * the ATRAC3 decoder would parse it as WAV format extra-data. */ + rmctx->extradata_size = 14; + memcpy(rmctx->codec_extradata, &id3->id3v1buf[0][0], 14); +} + +/* this is the codec entry point */ +enum codec_status codec_main(void) +{ + static size_t buff_size; + int datasize, res, frame_counter, total_frames, seek_frame_offset; + uint8_t *bit_buffer; + int elapsed = 0; + size_t resume_offset = ci->id3->offset; + +next_track: + if (codec_init()) { + DEBUGF("codec init failed\n"); + return CODEC_ERROR; + } + while (!*ci->taginfo_ready && !ci->stop_codec) + ci->sleep(1); + + codec_set_replaygain(ci->id3); + ci->memset(&rmctx,0,sizeof(RMContext)); + ci->memset(&q,0,sizeof(ATRAC3Context)); + + init_rm(&rmctx, ci->id3); + + ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); + ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */ + ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + + res =atrac3_decode_init(&q, &rmctx); + if(res < 0) { + DEBUGF("failed to initialize atrac decoder\n"); + return CODEC_ERROR; + } + + /* check for a mid-track resume and force a seek time accordingly */ + if(resume_offset > ci->id3->first_frame_offset) { + resume_offset -= ci->id3->first_frame_offset; + /* calculate resume_offset in frames */ + resume_offset = (int)resume_offset / FRAMESIZE; + ci->seek_time = (int)resume_offset * ((FRAMESIZE * 8)/BITRATE); + } + total_frames = (ci->id3->filesize - ci->id3->first_frame_offset) / FRAMESIZE; + frame_counter = 0; + + ci->set_elapsed(0); + ci->advance_buffer(ci->id3->first_frame_offset); + + /* The main decoder loop */ +seek_start : + while(frame_counter < total_frames) + { + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); + + ci->yield(); + if (ci->stop_codec || ci->new_track) + goto done; + + if (ci->seek_time) { + ci->set_elapsed(ci->seek_time); + + /* Do not allow seeking beyond the file's length */ + if ((unsigned) ci->seek_time > ci->id3->length) { + ci->seek_complete(); + goto done; + } + + /* Seek to the start of the track */ + if (ci->seek_time == 1) { + ci->set_elapsed(0); + ci->seek_complete(); + ci->seek_buffer(ci->id3->first_frame_offset); + elapsed = 0; + goto seek_start; + } + seek_frame_offset = (ci->seek_time * BITRATE) / (8 * FRAMESIZE); + frame_counter = seek_frame_offset; + ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE); + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); + elapsed = ci->seek_time; + + ci->set_elapsed(elapsed); + ci->seek_complete(); + } + + res = atrac3_decode_frame(&rmctx, &q, &datasize, bit_buffer, FRAMESIZE); + + if(res != (int)FRAMESIZE) { + DEBUGF("codec error\n"); + return CODEC_ERROR; + } + + if(datasize) + ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, q.samples_per_frame / rmctx.nb_channels); + + elapsed += (FRAMESIZE * 8) / BITRATE; + ci->set_elapsed(elapsed); + + ci->advance_buffer(FRAMESIZE); + frame_counter++; + } + + done: + if (ci->request_next_track()) + goto next_track; + + return CODEC_OK; +} diff --git a/apps/codecs/codecs.make b/apps/codecs/codecs.make index 0c3d2fade0..633f35b273 100644 --- a/apps/codecs/codecs.make +++ b/apps/codecs/codecs.make @@ -87,6 +87,7 @@ $(CODECDIR)/cook.codec : $(CODECDIR)/libcook.a $(CODECDIR)/librm.a $(CODECDIR)/raac.codec : $(CODECDIR)/libfaad.a $(CODECDIR)/librm.a $(CODECDIR)/a52_rm.codec : $(CODECDIR)/liba52.a $(CODECDIR)/librm.a $(CODECDIR)/atrac3_rm.codec : $(CODECDIR)/libatrac.a $(CODECDIR)/librm.a +$(CODECDIR)/atrac3_oma.codec : $(CODECDIR)/libatrac.a $(CODECDIR)/aiff.codec : $(CODECDIR)/libpcm.a $(CODECDIR)/wav.codec : $(CODECDIR)/libpcm.a diff --git a/apps/filetypes.c b/apps/filetypes.c index 671a8d5f1b..3feb0f5ab8 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -98,6 +98,8 @@ static const struct filetype inbuilt_filetypes[] = { { "tmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "tm8", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "tm2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { "oma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { "aa3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, #endif { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, diff --git a/apps/metadata.c b/apps/metadata.c index 6281a8526c..619a06e72d 100644 --- a/apps/metadata.c +++ b/apps/metadata.c @@ -162,6 +162,9 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = /* Atari TM2 File */ [AFMT_TM2] = AFMT_ENTRY("TM2", "asap", NULL, "tm2\0" ), + /* Atrac3 in Sony OMA Container */ + [AFMT_OMA_ATRAC3] = + AFMT_ENTRY("ATRAC3", "atrac3_oma", NULL, "oma\0aa3\0" ), #endif }; @@ -437,6 +440,13 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname) return false; } break; + case AFMT_OMA_ATRAC3: + if (!get_oma_metadata(fd, id3)) + { + DEBUGF("get_oma_metadata error\n"); + return false; + } + break; #endif /* CONFIG_CODEC == SWCODEC */ diff --git a/apps/metadata.h b/apps/metadata.h index 9b9cf9dfd1..b83c5497bf 100644 --- a/apps/metadata.h +++ b/apps/metadata.h @@ -77,6 +77,7 @@ enum AFMT_TMC, /* Atari 8bit tmc format */ AFMT_TM8, /* Atari 8bit tm8 format */ AFMT_TM2, /* Atari 8bit tm2 format */ + AFMT_OMA_ATRAC3, /* Atrac3 in Sony OMA container */ #endif /* add new formats at any index above this line to have a sensible order - diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h index e8f1832b87..aa07101269 100644 --- a/apps/metadata/metadata_parsers.h +++ b/apps/metadata/metadata_parsers.h @@ -41,4 +41,5 @@ bool get_asf_metadata(int fd, struct mp3entry* id3); bool get_asap_metadata(int fd, struct mp3entry* id3); bool get_rm_metadata(int fd, struct mp3entry* id3); bool get_nsf_metadata(int fd, struct mp3entry* id3); +bool get_oma_metadata(int fd, struct mp3entry* id3); diff --git a/apps/metadata/oma.c b/apps/metadata/oma.c new file mode 100644 index 0000000000..695ae0b114 --- /dev/null +++ b/apps/metadata/oma.c @@ -0,0 +1,188 @@ +/* + * Sony OpenMG (OMA) demuxer + * + * Copyright (c) 2008 Maxim Poliakovski + * 2008 Benjamin Larsson + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file oma.c + * This is a demuxer for Sony OpenMG Music files + * + * Known file extensions: ".oma", "aa3" + * The format of such files consists of three parts: + * - "ea3" header carrying overall info and metadata. + * - "EA3" header is a Sony-specific header containing information about + * the OpenMG file: codec type (usually ATRAC, can also be MP3 or WMA), + * codec specific info (packet size, sample rate, channels and so on) + * and DRM related info (file encryption, content id). + * - Sound data organized in packets follow the EA3 header + * (can be encrypted using the Sony DRM!). + * + * LIMITATIONS: This version supports only plain (unencrypted) OMA files. + * If any DRM-protected (encrypted) file is encountered you will get the + * corresponding error message. Try to remove the encryption using any + * Sony software (for example SonicStage). + * CODEC SUPPORT: Only ATRAC3 codec is currently supported! + */ + +#include +#include +#include +#include "metadata.h" +#include "metadata_parsers.h" + +#define EA3_HEADER_SIZE 96 + +#if 0 +#define DEBUGF printf +#else +#define DEBUGF(...) +#endif + +/* Various helper macros taken from ffmpeg for reading * + * and writing buffers with a specified endianess. */ +# define AV_RB16(x) \ + ((((const uint8_t*)(x))[0] << 8) | \ + ((const uint8_t*)(x))[1]) +# define AV_RB24(x) \ + ((((const uint8_t*)(x))[0] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[2]) +# define AV_RB32(x) \ + ((((const uint8_t*)(x))[0] << 24) | \ + (((const uint8_t*)(x))[1] << 16) | \ + (((const uint8_t*)(x))[2] << 8) | \ + ((const uint8_t*)(x))[3]) +# define AV_WL32(p, d) do { \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + ((uint8_t*)(p))[3] = (d)>>24; \ + } while(0) +# define AV_WL16(p, d) do { \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + } while(0) + +/* Different codecs that could be present in a Sony OMA * + * container file. */ +enum { + OMA_CODECID_ATRAC3 = 0, + OMA_CODECID_ATRAC3P = 1, + OMA_CODECID_MP3 = 3, + OMA_CODECID_LPCM = 4, + OMA_CODECID_WMA = 5, +}; + +/* FIXME: This functions currently read different file * + * parameters required for decoding. It still * + * does not read the metadata - which should be * + * present in the ea3 (first) header. The * + * metadata in ea3 is stored as a variation of * + * the ID3v2 metadata format. */ +int oma_read_header(int fd, struct mp3entry* id3) +{ + static const uint16_t srate_tab[6] = {320,441,480,882,960,0}; + int ret, ea3_taglen, EA3_pos, jsflag; + uint32_t codec_params; + int16_t eid; + uint8_t buf[EA3_HEADER_SIZE]; + + ret = read(fd, buf, 10); + if (ret != 10) + return -1; + + ea3_taglen = ((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f); + + EA3_pos = ea3_taglen + 10; + if (buf[5] & 0x10) + EA3_pos += 10; + + lseek(fd, EA3_pos, SEEK_SET); + ret = read(fd, buf, EA3_HEADER_SIZE); + if (ret != EA3_HEADER_SIZE) + return -1; + + if (memcmp(buf, ((const uint8_t[]){'E', 'A', '3'}),3) || buf[4] != 0 || buf[5] != EA3_HEADER_SIZE) { + DEBUGF("Couldn't find the EA3 header !\n"); + return -1; + } + + eid = AV_RB16(&buf[6]); + if (eid != -1 && eid != -128) { + DEBUGF("Encrypted file! Eid: %d\n", eid); + return -1; + } + + codec_params = AV_RB24(&buf[33]); + + switch (buf[32]) { + case OMA_CODECID_ATRAC3: + id3->frequency = srate_tab[(codec_params >> 13) & 7]*100; + if (id3->frequency != 44100) { + DEBUGF("Unsupported sample rate, send sample file to developers: %d\n", samplerate); + return -1; + } + + id3->bytesperframe = (codec_params & 0x3FF) * 8; + id3->codectype = AFMT_OMA_ATRAC3; + jsflag = (codec_params >> 17) & 1; /* get stereo coding mode, 1 for joint-stereo */ + + id3->bitrate = id3->frequency * id3->bytesperframe * 8 / (1024 * 1000); + + /* fake the atrac3 extradata (wav format, makes stream copy to wav work) */ + /* ATRAC3 expects and extra-data size of 14 bytes for wav format; extra-data size * + * is stored in ATRAC3Context before initializing the decoder. See atrac3_oma.codec. * + * We use id3v2buf to hold the (fake) extra-data provided from the container. */ + + AV_WL16(&id3->id3v1buf[0][0], 1); // always 1 + AV_WL32(&id3->id3v1buf[0][2], id3->frequency); // samples rate + AV_WL16(&id3->id3v1buf[0][6], jsflag); // coding mode + AV_WL16(&id3->id3v1buf[0][8], jsflag); // coding mode + AV_WL16(&id3->id3v1buf[0][10], 1); // always 1 + AV_WL16(&id3->id3v1buf[0][12], 0); // always 0 + + DEBUGF("sample_rate = %d\n", id3->frequency); + DEBUGF("frame_size = %d\n", id3->bytesperframe); + DEBUGF("stereo_coding_mode = %d\n", jsflag); + break; + default: + DEBUGF("Unsupported codec %d!\n",buf[32]); + return -1; + break; + } + + /* Store the the offset of the first audio frame, to be able to seek to it * + * directly in atrac3_oma.codec. */ + id3->first_frame_offset = EA3_pos + EA3_HEADER_SIZE; + return 0; +} + +bool get_oma_metadata(int fd, struct mp3entry* id3) +{ + if(oma_read_header(fd, id3) < 0) + return false; + + /* Currently, there's no means of knowing the duration * + * directly from the the file so we calculate it. */ + id3->filesize = filesize(fd); + id3->length = ((id3->filesize - id3->first_frame_offset) * 8) / id3->bitrate; + return true; +} -- cgit v1.2.3