mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
Multichannel mp3 in mp4 support ISO/IEC 14496-3:2001/FPDAM 3 (MP3onMP4)
Derived from MPlayer patch by Larry Ruedisueli Originally committed as revision 3955 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
parent
171d7d788a
commit
d2a7718df9
@ -145,6 +145,7 @@ void avcodec_register_all(void)
|
||||
register_avcodec(&mp2_decoder);
|
||||
register_avcodec(&mp3_decoder);
|
||||
register_avcodec(&mp3adu_decoder);
|
||||
register_avcodec(&mp3on4_decoder);
|
||||
register_avcodec(&mace3_decoder);
|
||||
register_avcodec(&mace6_decoder);
|
||||
register_avcodec(&huffyuv_decoder);
|
||||
|
@ -17,7 +17,7 @@ extern "C" {
|
||||
|
||||
#define FFMPEG_VERSION_INT 0x000409
|
||||
#define FFMPEG_VERSION "0.4.9-pre1"
|
||||
#define LIBAVCODEC_BUILD 4742
|
||||
#define LIBAVCODEC_BUILD 4743
|
||||
|
||||
#define LIBAVCODEC_VERSION_INT FFMPEG_VERSION_INT
|
||||
#define LIBAVCODEC_VERSION FFMPEG_VERSION
|
||||
@ -162,6 +162,7 @@ enum CodecID {
|
||||
CODEC_ID_SONIC_LS,
|
||||
CODEC_ID_FLAC,
|
||||
CODEC_ID_MP3ADU,
|
||||
CODEC_ID_MP3ON4,
|
||||
|
||||
CODEC_ID_MPEG2TS= 0x20000, /* _FAKE_ codec to indicate a raw MPEG2 transport
|
||||
stream (only used by libavformat) */
|
||||
@ -1946,6 +1947,7 @@ extern AVCodec png_decoder;
|
||||
extern AVCodec mp2_decoder;
|
||||
extern AVCodec mp3_decoder;
|
||||
extern AVCodec mp3adu_decoder;
|
||||
extern AVCodec mp3on4_decoder;
|
||||
extern AVCodec mace3_decoder;
|
||||
extern AVCodec mace6_decoder;
|
||||
extern AVCodec huffyuv_decoder;
|
||||
|
@ -120,6 +120,15 @@ typedef struct MPADecodeContext {
|
||||
unsigned int dither_state;
|
||||
} MPADecodeContext;
|
||||
|
||||
/**
|
||||
* Context for MP3On4 decoder
|
||||
*/
|
||||
typedef struct MP3On4DecodeContext {
|
||||
int frames; ///< number of mp3 frames per block (number of mp3 decoder instances)
|
||||
int chan_cfg; ///< channel config number
|
||||
MPADecodeContext *mp3decctx[5]; ///< MPADecodeContext for every decoder instance
|
||||
} MP3On4DecodeContext;
|
||||
|
||||
/* layer 3 "granule" */
|
||||
typedef struct GranuleDef {
|
||||
uint8_t scfsi;
|
||||
@ -2678,6 +2687,170 @@ static int decode_frame_adu(AVCodecContext * avctx,
|
||||
}
|
||||
|
||||
|
||||
/* Next 3 arrays are indexed by channel config number (passed via codecdata) */
|
||||
static int mp3Frames[16] = {0,1,1,2,3,3,4,5,2}; /* number of mp3 decoder instances */
|
||||
static int mp3Channels[16] = {0,1,2,3,4,5,6,8,4}; /* total output channels */
|
||||
/* offsets into output buffer, assume output order is FL FR BL BR C LFE */
|
||||
static int chan_offset[9][5] = {
|
||||
{0},
|
||||
{0}, // C
|
||||
{0}, // FLR
|
||||
{2,0}, // C FLR
|
||||
{2,0,3}, // C FLR BS
|
||||
{4,0,2}, // C FLR BLRS
|
||||
{4,0,2,5}, // C FLR BLRS LFE
|
||||
{4,0,2,6,5}, // C FLR BLRS BLR LFE
|
||||
{0,2} // FLR BLRS
|
||||
};
|
||||
|
||||
|
||||
static int decode_init_mp3on4(AVCodecContext * avctx)
|
||||
{
|
||||
MP3On4DecodeContext *s = avctx->priv_data;
|
||||
int i;
|
||||
|
||||
if ((avctx->extradata_size < 2) || (avctx->extradata == NULL)) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Codec extradata missing or too short.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->chan_cfg = (((unsigned char *)avctx->extradata)[1] >> 3) & 0x0f;
|
||||
s->frames = mp3Frames[s->chan_cfg];
|
||||
if(!s->frames) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Invalid channel config number.\n");
|
||||
return -1;
|
||||
}
|
||||
avctx->channels = mp3Channels[s->chan_cfg];
|
||||
|
||||
/* Init the first mp3 decoder in standard way, so that all tables get builded
|
||||
* We replace avctx->priv_data with the context of the first decoder so that
|
||||
* decode_init() does not have to be changed.
|
||||
* Other decoders will be inited here copying data from the first context
|
||||
*/
|
||||
// Allocate zeroed memory for the first decoder context
|
||||
s->mp3decctx[0] = av_mallocz(sizeof(MPADecodeContext));
|
||||
// Put decoder context in place to make init_decode() happy
|
||||
avctx->priv_data = s->mp3decctx[0];
|
||||
decode_init(avctx);
|
||||
// Restore mp3on4 context pointer
|
||||
avctx->priv_data = s;
|
||||
s->mp3decctx[0]->adu_mode = 1; // Set adu mode
|
||||
|
||||
/* Create a separate codec/context for each frame (first is already ok).
|
||||
* Each frame is 1 or 2 channels - up to 5 frames allowed
|
||||
*/
|
||||
for (i = 1; i < s->frames; i++) {
|
||||
s->mp3decctx[i] = av_mallocz(sizeof(MPADecodeContext));
|
||||
s->mp3decctx[i]->compute_antialias = s->mp3decctx[0]->compute_antialias;
|
||||
s->mp3decctx[i]->inbuf = &s->mp3decctx[i]->inbuf1[0][BACKSTEP_SIZE];
|
||||
s->mp3decctx[i]->inbuf_ptr = s->mp3decctx[i]->inbuf;
|
||||
s->mp3decctx[i]->adu_mode = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int decode_close_mp3on4(AVCodecContext * avctx)
|
||||
{
|
||||
MP3On4DecodeContext *s = avctx->priv_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->frames; i++)
|
||||
if (s->mp3decctx[i])
|
||||
av_free(s->mp3decctx[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame_mp3on4(AVCodecContext * avctx,
|
||||
void *data, int *data_size,
|
||||
uint8_t * buf, int buf_size)
|
||||
{
|
||||
MP3On4DecodeContext *s = avctx->priv_data;
|
||||
MPADecodeContext *m;
|
||||
int len, out_size = 0;
|
||||
uint32_t header;
|
||||
OUT_INT *out_samples = data;
|
||||
OUT_INT decoded_buf[MPA_FRAME_SIZE * MPA_MAX_CHANNELS];
|
||||
OUT_INT *outptr, *bp;
|
||||
int fsize;
|
||||
unsigned char *start2 = buf, *start;
|
||||
int fr, i, j, n;
|
||||
int off = avctx->channels;
|
||||
int *coff = chan_offset[s->chan_cfg];
|
||||
|
||||
len = buf_size;
|
||||
|
||||
// Discard too short frames
|
||||
if (buf_size < HEADER_SIZE) {
|
||||
*data_size = 0;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
// If only one decoder interleave is not needed
|
||||
outptr = s->frames == 1 ? out_samples : decoded_buf;
|
||||
|
||||
for (fr = 0; fr < s->frames; fr++) {
|
||||
start = start2;
|
||||
fsize = (start[0] << 4) | (start[1] >> 4);
|
||||
start2 += fsize;
|
||||
if (fsize > len)
|
||||
fsize = len;
|
||||
len -= fsize;
|
||||
if (fsize > MPA_MAX_CODED_FRAME_SIZE)
|
||||
fsize = MPA_MAX_CODED_FRAME_SIZE;
|
||||
m = s->mp3decctx[fr];
|
||||
assert (m != NULL);
|
||||
/* copy original to new */
|
||||
m->inbuf_ptr = m->inbuf + fsize;
|
||||
memcpy(m->inbuf, start, fsize);
|
||||
|
||||
// Get header
|
||||
header = (m->inbuf[0] << 24) | (m->inbuf[1] << 16) |
|
||||
(m->inbuf[2] << 8) | m->inbuf[3] | 0xfff00000;
|
||||
|
||||
if (ff_mpa_check_header(header) < 0) { // Bad header, discard block
|
||||
*data_size = 0;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
decode_header(m, header);
|
||||
mp_decode_frame(m, decoded_buf);
|
||||
|
||||
n = MPA_FRAME_SIZE * m->nb_channels;
|
||||
out_size += n * sizeof(OUT_INT);
|
||||
if(s->frames > 1) {
|
||||
/* interleave output data */
|
||||
bp = out_samples + coff[fr];
|
||||
if(m->nb_channels == 1) {
|
||||
for(j = 0; j < n; j++) {
|
||||
*bp = decoded_buf[j];
|
||||
bp += off;
|
||||
}
|
||||
} else {
|
||||
for(j = 0; j < n; j++) {
|
||||
bp[0] = decoded_buf[j++];
|
||||
bp[1] = decoded_buf[j];
|
||||
bp += off;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* update codec info */
|
||||
avctx->sample_rate = s->mp3decctx[0]->sample_rate;
|
||||
avctx->frame_size= buf_size;
|
||||
avctx->bit_rate = 0;
|
||||
for (i = 0; i < s->frames; i++)
|
||||
avctx->bit_rate += s->mp3decctx[i]->bit_rate;
|
||||
|
||||
*data_size = out_size;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
|
||||
AVCodec mp2_decoder =
|
||||
{
|
||||
"mp2",
|
||||
@ -2716,3 +2889,16 @@ AVCodec mp3adu_decoder =
|
||||
decode_frame_adu,
|
||||
CODEC_CAP_PARSE_ONLY,
|
||||
};
|
||||
|
||||
AVCodec mp3on4_decoder =
|
||||
{
|
||||
"mp3on4",
|
||||
CODEC_TYPE_AUDIO,
|
||||
CODEC_ID_MP3ON4,
|
||||
sizeof(MP3On4DecodeContext),
|
||||
decode_init_mp3on4,
|
||||
NULL,
|
||||
decode_close_mp3on4,
|
||||
decode_frame_mp3on4,
|
||||
0
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user