forked from len0rd/rockbox
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
This commit is contained in:
parent
5039cc716d
commit
23ac6ffaa1
9 changed files with 363 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -13,6 +13,7 @@ cook.c
|
|||
raac.c
|
||||
a52_rm.c
|
||||
atrac3_rm.c
|
||||
atrac3_oma.c
|
||||
mpc.c
|
||||
wma.c
|
||||
sid.c
|
||||
|
|
158
apps/codecs/atrac3_oma.c
Normal file
158
apps/codecs/atrac3_oma.c
Normal file
|
@ -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 <string.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 -
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
188
apps/metadata/oma.c
Normal file
188
apps/metadata/oma.c
Normal file
|
@ -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 <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue