Files
luban-lite-t3e-pro/packages/artinchip/mpp/middle_media/base/parser/mp3/mp3.c
2025-09-30 11:56:06 +08:00

577 lines
16 KiB
C

/*
* Copyright (C) 2020-2023 ArtInChip Technology Co. Ltd
*
* author: <jun.ma@artinchip.com>
* Desc: mp3_parser
*/
#include <stdlib.h>
#include <inttypes.h>
#include "aic_stream.h"
#include "mp3.h"
#include "mpp_mem.h"
#include "mpp_log.h"
const uint16_t avpriv_mpa_freq_tab[3] = { 44100, 48000, 32000 };
const uint16_t avpriv_mpa_bitrate_tab[2][3][15] = {
{ {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448},
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384},
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},
{ {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}
}
};
static int stream_skip(struct aic_stream* s, int len)
{
return aic_stream_seek(s, len, SEEK_CUR);
}
static unsigned int r8(struct aic_stream* s)
{
unsigned char val;
aic_stream_read(s, &val, 1);
return val;
}
static unsigned int rb16(struct aic_stream* s)
{
unsigned int val;
val = r8(s) << 8;
val |= r8(s);
return val;
}
static unsigned int rb24(struct aic_stream* s)
{
unsigned int val;
val = rb16(s) << 16;
val |= r8(s);
return val;
}
static unsigned int rb32(struct aic_stream* s)
{
unsigned int val;
val = rb16(s) << 16;
val |= rb16(s);
return val;
}
static int id3v2_match(const uint8_t *buf, const char *magic)
{
return buf[0] == magic[0] &&
buf[1] == magic[1] &&
buf[2] == magic[2] &&
buf[3] != 0xff &&
buf[4] != 0xff &&
(buf[6] & 0x80) == 0 &&
(buf[7] & 0x80) == 0 &&
(buf[8] & 0x80) == 0 &&
(buf[9] & 0x80) == 0;
}
/* fast header check for resync */
static int mpa_check_header(uint32_t header)
{
/* header */
if ((header & 0xffe00000) != 0xffe00000)
return -1;
/* version check */
if ((header & (3<<19)) == 1<<19)
return -1;
/* layer check */
if ((header & (3<<17)) == 0)
return -1;
/* bit rate */
if ((header & (0xf<<12)) == 0xf<<12)
return -1;
/* frequency */
if ((header & (3<<10)) == 3<<10)
return -1;
return 0;
}
int mpegaudio_decode_header(struct mpa_decode_header *s, uint32_t header)
{
int sample_rate, frame_size, mpeg25, padding;
int sample_rate_index, bitrate_index;
int ret;
ret = mpa_check_header(header);
if (ret < 0)
return ret;
if (header & (1<<20)) {
s->lsf = (header & (1<<19)) ? 0 : 1;
mpeg25 = 0;
} else {
s->lsf = 1;
mpeg25 = 1;
}
s->layer = 4 - ((header >> 17) & 3);
/* extract frequency */
sample_rate_index = (header >> 10) & 3;
if (sample_rate_index >= ARRAY_ELEMS(avpriv_mpa_freq_tab))
sample_rate_index = 0;
sample_rate = avpriv_mpa_freq_tab[sample_rate_index] >> (s->lsf + mpeg25);
sample_rate_index += 3 * (s->lsf + mpeg25);
s->sample_rate_index = sample_rate_index;
s->error_protection = ((header >> 16) & 1) ^ 1;
s->sample_rate = sample_rate;
s->nb_samples_per_frame = 1152;
if (s->layer == 1) {
s->nb_samples_per_frame = 384;
} else if(s->layer == 3 && s->lsf) {
s->nb_samples_per_frame = 576;
} else {
s->nb_samples_per_frame = 1152;
}
s->frame_duration = s->nb_samples_per_frame * 1000 * 1000 / s->sample_rate;
bitrate_index = (header >> 12) & 0xf;
padding = (header >> 9) & 1;
//extension = (header >> 8) & 1;
s->mode = (header >> 6) & 3;
s->mode_ext = (header >> 4) & 3;
//copyright = (header >> 3) & 1;
//original = (header >> 2) & 1;
//emphasis = header & 3;
if (s->mode == MPA_MONO)
s->nb_channels = 1;
else
s->nb_channels = 2;
if (bitrate_index != 0) {
frame_size = avpriv_mpa_bitrate_tab[s->lsf][s->layer - 1][bitrate_index];
s->bit_rate = frame_size * 1000;
switch(s->layer) {
case 1:
frame_size = (frame_size * 12000) / sample_rate;
frame_size = (frame_size + padding) * 4;
break;
case 2:
frame_size = (frame_size * 144000) / sample_rate;
frame_size += padding;
break;
default:
case 3:
frame_size = (frame_size * 144000) / (sample_rate << s->lsf);
frame_size += padding;
break;
}
s->frame_size = frame_size;
} else {
/* if no frame size computed, signal it */
return -1;
}
return 0;
}
static int check(struct aic_stream* stream, int64_t pos, uint32_t *ret_header)
{
int64_t ret;
uint8_t header_buf[4];
unsigned header;
struct mpa_decode_header sd;
ret = aic_stream_seek(stream, pos, SEEK_SET);
if (ret < 0)
return CHECK_SEEK_FAILED;
ret = aic_stream_read(stream, &header_buf[0], 4);
/* We should always find four bytes for a valid mpa header. */
if (ret < 4)
return CHECK_SEEK_FAILED;
//header = AV_RB32(&header_buf[0]);
header = MKBETAG(header_buf[0],header_buf[1],header_buf[2],header_buf[3]);
if (mpa_check_header(header) < 0)
return CHECK_WRONG_HEADER;
if (mpegaudio_decode_header(&sd, header) == 1)
return CHECK_WRONG_HEADER;
if (ret_header)
*ret_header = header;
return sd.frame_size;
}
static void mp3_parse_info_tag(struct aic_mp3_parser *s, uint32_t spf)
{
uint32_t v;
char version[10];
struct mp3_dec_context * mp3 = &s->ctx;
static const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}};
uint64_t fsize = aic_stream_size(s->stream);
fsize = fsize >= aic_stream_tell(s->stream) ? fsize - aic_stream_tell(s->stream) : 0;
/* Check for Xing / Info tag */
stream_skip(s->stream, xing_offtbl[s->header.lsf == 1][s->header.nb_channels == 1]);
v = rb32(s->stream);
mp3->is_cbr = v == MKBETAG('I', 'n', 'f', 'o');
if (v != MKBETAG('X', 'i', 'n', 'g') && !mp3->is_cbr)
return;
v = rb32(s->stream);
if (v & XING_FLAG_FRAMES)
mp3->frames = rb32(s->stream);
if (v & XING_FLAG_SIZE)
mp3->header_filesize = rb32(s->stream);
if (fsize && mp3->header_filesize) {
uint64_t min, delta;
min = MPP_MIN(fsize, mp3->header_filesize);
delta = MPP_MAX(fsize, mp3->header_filesize) - min;
if (fsize > mp3->header_filesize && delta > min >> 4) {
mp3->frames = 0;
logw("invalid concatenated file detected - using bitrate for duration\n");
} else if (delta > min >> 4) {
logw("filesize and duration do not match (growing file?)\n");
}
}
if (v & XING_FLAG_TOC) {
aic_stream_read(s->stream, mp3->xing_toc_index, XING_TOC_COUNT);
mp3->xing_toc = 1;
}
/* VBR quality */
if (v & XING_FLAC_QSCALE)
rb32(s->stream);
/* Encoder short version string */
memset(version, 0, sizeof(version));
aic_stream_read(s->stream, version, 9);
/* Info Tag revision + VBR method */
r8(s->stream);
/* Lowpass filter value */
r8(s->stream);
/* ReplayGain peak */
v = rb32(s->stream);
/* Radio ReplayGain */
v = rb16(s->stream);
/* Audiophile ReplayGain */
v = rb16(s->stream);
/* Encoding flags + ATH Type */
r8(s->stream);
/* if ABR {specified bitrate} else {minimal bitrate} */
r8(s->stream);
/* Encoder delays */
v = rb24(s->stream);
if (MKBETAG(version[0],version[1],version[2],version[3]) == MKBETAG('L', 'A', 'M', 'E')
|| MKBETAG(version[0],version[1],version[2],version[3]) == MKBETAG('L', 'a', 'v', 'f')
|| MKBETAG(version[0],version[1],version[2],version[3]) == MKBETAG('L', 'a', 'v', 'c')
) {
mp3->start_pad = v>>12;
mp3-> end_pad = v&4095;
}
/* Misc */
r8(s->stream);
/* MP3 gain */
r8(s->stream);
/* Preset and surround info */
rb16(s->stream);
/* Music length */
rb32(s->stream);
/* Music CRC */
rb16(s->stream);
/* Info Tag CRC */
v = rb16(s->stream);
}
static void mp3_parse_vbri_tag(struct aic_mp3_parser *s, int64_t base)
{
uint32_t v;
struct mp3_dec_context * mp3 = &s->ctx;
/* Check for VBRI tag (always 32 bytes after end of mpegaudio header) */
aic_stream_seek(s->stream, base + 4 + 32, SEEK_SET);
v = rb32(s->stream);
if (v == MKBETAG('V', 'B', 'R', 'I')) {
/* Check tag version */
if (rb16(s->stream) == 1) {
/* skip delay and quality */
stream_skip(s->stream, 4);
mp3->header_filesize = rb32(s->stream);
mp3->frames = rb32(s->stream);
}
}
}
static int mp3_parse_vbr_tags(struct aic_mp3_parser *s, int64_t base)
{
uint32_t v, spf;
int vbrtag_size = 0;
struct mp3_dec_context *mp3 = &s->ctx;
v = rb32(s->stream);
if (mpegaudio_decode_header(&s->header,v)) {
loge("error header");
return -1;
}
vbrtag_size = s->header.frame_size; /*size after encoding*/
spf = s->header.lsf ? 576 : 1152; /* Samples per frame, layer 3 ,before encoding */
mp3->frames = 0;
mp3->header_filesize = 0;
mp3_parse_info_tag(s, spf);
mp3_parse_vbri_tag(s, base);
if (!mp3->frames && !mp3->header_filesize) {
return -1;
}
printf("[%s:%d]frames:%u,header_filesize:%u\n",__FUNCTION__,__LINE__,mp3->frames,mp3->header_filesize);
/* Skip the vbr tag frame */
aic_stream_seek(s->stream, base + vbrtag_size, SEEK_SET);
//calculate total duration
if (mp3->frames) {
s->duration = mp3->frames * spf / s->header.sample_rate * 1000 * 1000;
printf("[%s:%d]sample_rate:%d,spf:%"PRIu32"\n",__FUNCTION__,__LINE__,s->header.sample_rate,spf);
}
//calculate avg bit rate
if (mp3->header_filesize && mp3->frames && !mp3->is_cbr) {
//s->header.bit_rate = mp3->header_filesize/st->duration;
}
return 0;
}
/*
mp3 file struct
| ID3V2 (may not contain)| frame_0, frame_1, ..., frame_n | ID3V1 |
*/
int mp3_read_header(struct aic_mp3_parser *s)
{
int found_header;
int64_t off;
unsigned char id3v2_header[ID3v2_HEADER_SIZE];
int len = 0;
int i =0;
int ret;
// proccess idv32 here,now do nothing,skip idv32
aic_stream_read(s->stream, id3v2_header, 10);
found_header = id3v2_match(id3v2_header, ID3v2_DEFAULT_MAGIC);
if (found_header) {
len = ((id3v2_header[6] & 0x7f) << 21) | ((id3v2_header[7] & 0x7f) << 14) | ((id3v2_header[8] & 0x7f) << 7) |(id3v2_header[9] & 0x7f);
aic_stream_seek(s->stream, len, SEEK_CUR);//skip id3v2 header
} else {
printf("this file do not contian id3v2\n");
aic_stream_seek(s->stream, 0, SEEK_SET);
}
off = aic_stream_tell(s->stream);
if (mp3_parse_vbr_tags(s, off) < 0) {
aic_stream_seek(s->stream, off, SEEK_SET);
}
off = aic_stream_tell(s->stream);
for (i = 0; i < 64 * 1024; i++) {
uint32_t header, header2;
int frame_size;
frame_size = check(s->stream, off + i, &header);
if (frame_size > 0) {
ret = aic_stream_seek(s->stream, off, SEEK_SET);
if (ret < 0)
return ret;
ret = check(s->stream, off + i + frame_size, &header2);
if (ret >= 0 &&
(header & MP3_MASK) == (header2 & MP3_MASK)) {
logw("Skipping %d bytes of junk at %"PRId64".\n", i, off);
ret = aic_stream_seek(s->stream, off + i, SEEK_SET);
if (ret < 0)
return ret;
break;
} else if (ret == CHECK_SEEK_FAILED) {
logw("Invalid frame size (%d): Could not seek to %"PRId64".\n", frame_size, off + i + frame_size);
return -1;
}
} else if (frame_size == CHECK_SEEK_FAILED) {
logw("Failed to read frame size: Could not seek to %"PRId64".\n",(int64_t) (i + 1024 + frame_size + 4));
return -1;
}
ret = aic_stream_seek(s->stream, off, SEEK_SET);
if (ret < 0)
return ret;
}
s->first_packet_pos = aic_stream_tell(s->stream);
s->ctx.filesize = aic_stream_size(s->stream);
if (!s->duration) {
s->duration = 1000 * 1000 * (s->ctx.filesize - s->first_packet_pos - ID3v1_TAG_SIZE) / (s->header.bit_rate/8);
printf("[%s:%d]filesize:%"PRId64",duration:%"PRId64"\n",__FUNCTION__,__LINE__,s->ctx.filesize,s->duration);
if (!s->ctx.is_cbr) {
logw("this is VBR");
}
}
printf("[%s:%d]filesize:%"PRId64",duration:%"PRId64"\n",__FUNCTION__,__LINE__,s->ctx.filesize,s->duration);
return 0;
}
int mp3_close(struct aic_mp3_parser *c)
{
return 0;
}
#define SEEK_WINDOW 4096
static int64_t mp3_sync(struct aic_mp3_parser *s, int64_t target_pos)
{
int dir = 1;
int64_t best_pos;
int best_score, i, j;
int64_t ret;
ret = aic_stream_seek(s->stream, target_pos, SEEK_SET);
if (ret < 0)
return ret;
best_pos = target_pos;
best_score = 999;
for (i = 0; i < SEEK_WINDOW; i++) {
int64_t pos = target_pos + (dir > 0 ? i - SEEK_WINDOW/4 : -i);
int64_t candidate = -1;
int score = 999;
if (pos < 0)
continue;
#define MIN_VALID 3
for (j = 0; j < MIN_VALID; j++) {
ret = check(s->stream, pos, NULL);
if (ret < 0) {
if (ret == CHECK_WRONG_HEADER) {
break;
} else if (ret == CHECK_SEEK_FAILED) {
loge("Could not seek to %"PRId64".\n", pos);
return -1;
}
}
if ((target_pos - pos)*dir <= 0 && FFABS(MIN_VALID/2-j) < score) {
candidate = pos;
score = FFABS(MIN_VALID/2-j);
}
pos += ret;
}
if (best_score > score && j == MIN_VALID) {
best_pos = candidate;
best_score = score;
if(score == 0)
break;
}
}
return aic_stream_seek(s->stream, best_pos, SEEK_SET);
}
int mp3_seek_packet(struct aic_mp3_parser *s, s64 seek_time)
{
struct mp3_dec_context *mp3 = &s->ctx;
int64_t best_pos;
int64_t pos;
//printf("[%s:%d]%"PRId64"\n",__FUNCTION__,__LINE__,seek_time);
if (mp3->xing_toc && mp3->header_filesize && !mp3->is_cbr) {
// NOTE: The MP3 TOC is not a precise lookup table. Accuracy is worse
//for bigger files.
logw("Using MP3 TOC to seek; may be imprecise.\n");
int toc_index = seek_time * 100 / s->duration;
toc_index = (toc_index < 0)?(0):(toc_index);
toc_index = (toc_index > 99)?(99):(toc_index);
pos = mp3->header_filesize * mp3->xing_toc_index[toc_index]/256 + s->first_packet_pos;
printf("[%s:%d]%d,%d,%u,%u\n",__FUNCTION__,__LINE__,toc_index,mp3->xing_toc_index[toc_index],mp3->header_filesize,s->first_packet_pos);
} else {
if (!mp3->is_cbr) {
logw("Using scaling to seek VBR MP3; may be imprecise.\n");
}
pos = (seek_time / 1000 / 1000) * s->header.bit_rate / 8 + s->first_packet_pos;
printf("[%s:%d]%"PRId64",%u,%u\n",__FUNCTION__,__LINE__,pos,mp3->header_filesize,s->first_packet_pos);
}
s->frame_id = seek_time / s->header.frame_duration;
printf("[%s:%d]frame_id:%"PRIu32"\n",__FUNCTION__,__LINE__,s->frame_id);
best_pos = mp3_sync(s, pos);
if (best_pos < 0)
return best_pos;
return 0;
}
//#define MP3_PACKET_SIZE 1024
int mp3_peek_packet(struct aic_mp3_parser *s, struct aic_parser_packet *pkt)
{
int64_t pos;
struct mp3_dec_context *mp3 = &s->ctx;
#ifdef MP3_PACKET_SIZE
#else
uint32_t v;
struct mpa_decode_header h;
#endif
pos = aic_stream_tell(s->stream);
if (pos >= mp3->filesize - ID3v1_TAG_SIZE) {
printf("[%s:%d]PARSER_EOS,%"PRId64",%"PRId64"\n",__FUNCTION__,__LINE__,pos,mp3->filesize);
return PARSER_EOS;
}
#ifdef MP3_PACKET_SIZE
pkt->size = MP3_PACKET_SIZE;
#else
// check header
v = rb32(s->stream);
if (mpa_check_header(v) < 0) {
loge("frame header error,resync header");
if (mp3_sync(s,pos) < 0) {
loge("resync header error");
return -1;
}
pos = aic_stream_tell(s->stream);
v = rb32(s->stream);
}
aic_stream_seek(s->stream,-4,SEEK_CUR);
// analize head get cur frame size
mpegaudio_decode_header(&h,v);
pkt->size = h.frame_size;
pkt->pts = s->frame_id * s->header.frame_duration;
#endif
pkt->type = MPP_MEDIA_TYPE_AUDIO;
return 0;
}
int mp3_read_packet(struct aic_mp3_parser *s, struct aic_parser_packet *pkt)
{
struct mp3_dec_context *mp3 = &s->ctx;
int64_t pos;
pos = aic_stream_tell(s->stream);
if (pos >= mp3->filesize - ID3v1_TAG_SIZE) {
return PARSER_EOS;
}
#ifdef MP3_PACKET_SIZE
pkt->size = aic_stream_read(s->stream,pkt->data,MP3_PACKET_SIZE);
#else
aic_stream_read(s->stream,pkt->data,pkt->size);
#endif
pos = aic_stream_tell(s->stream);
if (pos >= mp3->filesize - ID3v1_TAG_SIZE) {
pkt->flag |= PACKET_EOS;
}
s->frame_id++;
return 0;
}