Files
cmus/ip/aac.c
2026-03-29 14:01:52 +03:00

542 lines
13 KiB
C

/*
* Copyright 2008-2013 Various Authors
* Copyright 2006 dnk <dnk@bjum.net>
*
* 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 program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "../ip.h"
#include "../xmalloc.h"
#include "../debug.h"
#include "../id3.h"
#include "../comment.h"
#include "../read_wrapper.h"
#include "aac.h"
#include <neaacdec.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
/* FAAD_MIN_STREAMSIZE == 768, 6 == # of channels */
#define BUFFER_SIZE (FAAD_MIN_STREAMSIZE * 6 * 4)
struct aac_private {
char rbuf[BUFFER_SIZE];
int rbuf_len;
int rbuf_pos;
unsigned char channels;
unsigned long sample_rate;
long bitrate;
int object_type;
struct {
unsigned long samples;
unsigned long bytes;
} current;
char *overflow_buf;
int overflow_buf_len;
NeAACDecHandle decoder; /* typedef void * */
};
static inline int buffer_length(const struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
return priv->rbuf_len - priv->rbuf_pos;
}
static inline void *buffer_data(const struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
return priv->rbuf + priv->rbuf_pos;
}
static int buffer_fill(struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
int32_t n;
if (priv->rbuf_pos > 0) {
priv->rbuf_len = buffer_length(ip_data);
memmove(priv->rbuf, priv->rbuf + priv->rbuf_pos, priv->rbuf_len);
priv->rbuf_pos = 0;
}
if (priv->rbuf_len == BUFFER_SIZE)
return 1;
n = read_wrapper(ip_data, priv->rbuf + priv->rbuf_len, BUFFER_SIZE - priv->rbuf_len);
if (n == -1)
return -1;
if (n == 0)
return 0;
priv->rbuf_len += n;
return 1;
}
static inline void buffer_consume(struct input_plugin_data *ip_data, int n)
{
struct aac_private *priv = ip_data->private;
BUG_ON(n > buffer_length(ip_data));
priv->rbuf_pos += n;
}
static int buffer_fill_min(struct input_plugin_data *ip_data, int len)
{
int rc;
BUG_ON(len > BUFFER_SIZE);
while (buffer_length(ip_data) < len) {
rc = buffer_fill(ip_data);
if (rc <= 0)
return rc;
}
return 1;
}
/* 'data' must point to at least 6 bytes of data */
static inline int parse_frame(const unsigned char data[6])
{
int len;
/* http://www.audiocoding.com/modules/wiki/?page=ADTS */
/* first 12 bits must be set */
if (data[0] != 0xFF)
return 0;
if ((data[1] & 0xF0) != 0xF0)
return 0;
/* layer is always '00' */
if ((data[1] & 0x06) != 0x00)
return 0;
/* frame length is stored in 13 bits */
len = data[3] << 11; /* ..1100000000000 */
len |= data[4] << 3; /* ..xx11111111xxx */
len |= data[5] >> 5; /* ..xxxxxxxxxx111 */
len &= 0x1FFF; /* 13 bits */
return len;
}
/* scans forward to the next aac frame and makes sure
* the entire frame is in the buffer.
*/
static int buffer_fill_frame(struct input_plugin_data *ip_data)
{
unsigned char *data;
int rc, n, len;
int max = 32768;
while (1) {
/* need at least 6 bytes of data */
rc = buffer_fill_min(ip_data, 6);
if (rc <= 0)
return rc;
len = buffer_length(ip_data);
data = buffer_data(ip_data);
/* scan for a frame */
for (n = 0; n < len - 5; n++) {
/* give up after 32KB */
if (max-- == 0) {
d_print("no frame found!\n");
/* FIXME: set errno? */
return -1;
}
/* see if there's a frame at this location */
rc = parse_frame(data + n);
if (rc == 0)
continue;
/* found a frame, consume all data up to the frame */
buffer_consume(ip_data, n);
/* rc == frame length */
rc = buffer_fill_min(ip_data, rc);
if (rc <= 0)
return rc;
return 1;
}
/* consume what we used */
buffer_consume(ip_data, n);
}
/* not reached */
}
static void aac_get_channel_map(struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
NeAACDecFrameInfo frame_info;
void *buf;
int i;
ip_data->channel_map[0] = CHANNEL_POSITION_INVALID;
if (buffer_fill_frame(ip_data) <= 0)
return;
buf = NeAACDecDecode(priv->decoder, &frame_info, buffer_data(ip_data), buffer_length(ip_data));
NeAACDecPostSeekReset(priv->decoder, 0);
if (!buf || frame_info.error != 0 || frame_info.bytesconsumed <= 0
|| frame_info.channels > CHANNELS_MAX)
return;
for (i = 0; i < frame_info.channels; i++)
ip_data->channel_map[i] = channel_position_aac(frame_info.channel_position[i]);
}
static int aac_open(struct input_plugin_data *ip_data)
{
struct aac_private *priv;
NeAACDecConfigurationPtr neaac_cfg;
int ret, n;
/* init private struct */
const struct aac_private priv_init = {
.decoder = NeAACDecOpen(),
.bitrate = -1,
.object_type = -1
};
priv = xnew(struct aac_private, 1);
*priv = priv_init;
ip_data->private = priv;
/* set decoder config */
neaac_cfg = NeAACDecGetCurrentConfiguration(priv->decoder);
neaac_cfg->outputFormat = FAAD_FMT_16BIT; /* force 16 bit audio */
neaac_cfg->downMatrix = 0; /* NOT 5.1 -> stereo */
neaac_cfg->dontUpSampleImplicitSBR = 0; /* upsample, please! */
NeAACDecSetConfiguration(priv->decoder, neaac_cfg);
/* find a frame */
if (buffer_fill_frame(ip_data) <= 0) {
ret = -IP_ERROR_FILE_FORMAT;
goto out;
}
/* in case of a bug, make sure there is at least some data
* in the buffer for NeAACDecInit() to work with.
*/
if (buffer_fill_min(ip_data, 256) <= 0) {
d_print("not enough data\n");
ret = -IP_ERROR_FILE_FORMAT;
goto out;
}
/* init decoder, returns the length of the header (if any) */
n = NeAACDecInit(priv->decoder, buffer_data(ip_data), buffer_length(ip_data),
&priv->sample_rate, &priv->channels);
if (n < 0) {
d_print("NeAACDecInit failed\n");
ret = -IP_ERROR_FILE_FORMAT;
goto out;
}
d_print("sample rate %luhz, channels %u\n", priv->sample_rate, priv->channels);
if (!priv->sample_rate || !priv->channels) {
ret = -IP_ERROR_FILE_FORMAT;
goto out;
}
/* skip the header */
d_print("skipping header (%d bytes)\n", n);
buffer_consume(ip_data, n);
/*NeAACDecInitDRM(priv->decoder, priv->sample_rate, priv->channels);*/
ip_data->sf = sf_rate(priv->sample_rate) | sf_channels(priv->channels) | sf_bits(16) | sf_signed(1);
ip_data->sf |= sf_host_endian();
aac_get_channel_map(ip_data);
return 0;
out:
NeAACDecClose(priv->decoder);
free(priv);
return ret;
}
static int aac_close(struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
NeAACDecClose(priv->decoder);
free(priv);
ip_data->private = NULL;
return 0;
}
/* returns -1 on fatal errors
* returns -2 on non-fatal errors
* 0 on eof
* number of bytes put in 'buffer' on success */
static int decode_one_frame(struct input_plugin_data *ip_data, void *buffer, int count)
{
struct aac_private *priv = ip_data->private;
unsigned char *aac_data;
unsigned int aac_data_size;
NeAACDecFrameInfo frame_info;
char *sample_buf;
int bytes, rc;
rc = buffer_fill_frame(ip_data);
if (rc <= 0)
return rc;
aac_data = buffer_data(ip_data);
aac_data_size = buffer_length(ip_data);
/* aac data -> raw pcm */
sample_buf = NeAACDecDecode(priv->decoder, &frame_info, aac_data, aac_data_size);
if (frame_info.error == 0 && frame_info.samples > 0) {
priv->current.samples += frame_info.samples;
priv->current.bytes += frame_info.bytesconsumed;
}
buffer_consume(ip_data, frame_info.bytesconsumed);
if (!sample_buf || frame_info.bytesconsumed <= 0) {
d_print("fatal error: %s\n", NeAACDecGetErrorMessage(frame_info.error));
errno = EINVAL;
return -1;
}
if (frame_info.error != 0) {
d_print("frame error: %s\n", NeAACDecGetErrorMessage(frame_info.error));
return -2;
}
if (frame_info.samples <= 0)
return -2;
if (frame_info.channels != priv->channels || frame_info.samplerate != priv->sample_rate) {
d_print("invalid channel or sample_rate count\n");
return -2;
}
/* 16-bit samples */
bytes = frame_info.samples * 2;
if (bytes > count) {
/* decoded too much, keep overflow */
priv->overflow_buf = sample_buf + count;
priv->overflow_buf_len = bytes - count;
memcpy(buffer, sample_buf, count);
return count;
} else {
memcpy(buffer, sample_buf, bytes);
}
return bytes;
}
static int aac_read(struct input_plugin_data *ip_data, char *buffer, int count)
{
struct aac_private *priv = ip_data->private;
int rc;
/* use overflow from previous call (if any) */
if (priv->overflow_buf_len) {
int len = priv->overflow_buf_len;
if (len > count)
len = count;
memcpy(buffer, priv->overflow_buf, len);
priv->overflow_buf += len;
priv->overflow_buf_len -= len;
return len;
}
do {
rc = decode_one_frame(ip_data, buffer, count);
} while (rc == -2);
return rc;
}
static int aac_seek(struct input_plugin_data *ip_data, double offset)
{
return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
}
static int aac_read_comments(struct input_plugin_data *ip_data,
struct keyval **comments)
{
GROWING_KEYVALS(c);
struct id3tag id3;
int rc, fd, i;
fd = open(ip_data->filename, O_RDONLY);
if (fd == -1)
return -1;
id3_init(&id3);
rc = id3_read_tags(&id3, fd, ID3_V1 | ID3_V2);
if (rc == -1) {
d_print("error: %s\n", strerror(errno));
goto out;
}
for (i = 0; i < NUM_ID3_KEYS; i++) {
char *val = id3_get_comment(&id3, i);
if (val)
comments_add(&c, id3_key_names[i], val);
}
out:
close(fd);
id3_free(&id3);
keyvals_terminate(&c);
*comments = c.keyvals;
return 0;
}
static int aac_duration(struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
NeAACDecFrameInfo frame_info;
int samples = 0, bytes = 0, frames = 0;
off_t file_size;
file_size = lseek(ip_data->fd, 0, SEEK_END);
if (file_size == -1)
return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
/* Seek to the middle of the file. There is almost always silence at
* the beginning, which gives wrong results. */
if (lseek(ip_data->fd, file_size/2, SEEK_SET) == -1)
return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
priv->rbuf_pos = 0;
priv->rbuf_len = 0;
/* guess track length by decoding the first 10 frames */
while (frames < 10) {
if (buffer_fill_frame(ip_data) <= 0)
break;
NeAACDecDecode(priv->decoder, &frame_info,
buffer_data(ip_data), buffer_length(ip_data));
if (frame_info.error == 0 && frame_info.samples > 0) {
samples += frame_info.samples;
bytes += frame_info.bytesconsumed;
frames++;
}
if (frame_info.bytesconsumed == 0)
break;
buffer_consume(ip_data, frame_info.bytesconsumed);
}
if (frames == 0)
return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
NeAACDecPostSeekReset(priv->decoder, 0);
samples /= frames;
samples /= priv->channels;
bytes /= frames;
/* 8 * file_size / duration */
priv->bitrate = (8 * bytes * priv->sample_rate) / samples;
priv->object_type = frame_info.object_type;
return ((file_size / bytes) * samples) / priv->sample_rate;
}
static long aac_bitrate(struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
return priv->bitrate != -1 ? priv->bitrate : -IP_ERROR_FUNCTION_NOT_SUPPORTED;
}
static long aac_current_bitrate(struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
long bitrate = -1;
if (priv->current.samples > 0) {
priv->current.samples /= priv->channels;
bitrate = (8 * priv->current.bytes * priv->sample_rate) / priv->current.samples;
priv->current.samples = 0;
priv->current.bytes = 0;
}
return bitrate;
}
static char *aac_codec(struct input_plugin_data *ip_data)
{
return xstrdup("aac");
}
static const char *object_type_to_str(int object_type)
{
switch (object_type) {
case MAIN: return "Main";
case LC: return "LC";
case SSR: return "SSR";
case LTP: return "LTP";
case HE_AAC: return "HE";
case ER_LC: return "ER-LD";
case ER_LTP: return "ER-LTP";
case LD: return "LD";
case DRM_ER_LC: return "DRM-ER-LC";
}
return NULL;
}
static char *aac_codec_profile(struct input_plugin_data *ip_data)
{
struct aac_private *priv = ip_data->private;
const char *profile = object_type_to_str(priv->object_type);
return profile ? xstrdup(profile) : NULL;
}
const struct input_plugin_ops ip_ops = {
.open = aac_open,
.close = aac_close,
.read = aac_read,
.seek = aac_seek,
.read_comments = aac_read_comments,
.duration = aac_duration,
.bitrate = aac_bitrate,
.bitrate_current = aac_current_bitrate,
.codec = aac_codec,
.codec_profile = aac_codec_profile
};
const int ip_priority = 50;
const char * const ip_extensions[] = { "aac", NULL };
const char * const ip_mime_types[] = { "audio/aac", "audio/aacp", NULL };
const struct input_plugin_opt ip_options[] = { { NULL } };
const unsigned ip_abi_version = IP_ABI_VERSION;