Mercurial > libavcodec.hg
changeset 2507:0334caf0f0ce libavcodec
Multichannel mp3 in mp4 support ISO/IEC 14496-3:2001/FPDAM 3 (MP3onMP4)
Derived from MPlayer patch by Larry Ruedisueli
author | rtognimp |
---|---|
date | Tue, 15 Feb 2005 20:58:32 +0000 |
parents | 9404bbf9de07 |
children | b0ec6f51b5bd |
files | allcodecs.c avcodec.h mpegaudiodec.c |
diffstat | 3 files changed, 190 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/allcodecs.c Sun Feb 13 23:36:04 2005 +0000 +++ b/allcodecs.c Tue Feb 15 20:58:32 2005 +0000 @@ -145,6 +145,7 @@ 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);
--- a/avcodec.h Sun Feb 13 23:36:04 2005 +0000 +++ b/avcodec.h Tue Feb 15 20:58:32 2005 +0000 @@ -17,7 +17,7 @@ #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 @@ 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 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;
--- a/mpegaudiodec.c Sun Feb 13 23:36:04 2005 +0000 +++ b/mpegaudiodec.c Tue Feb 15 20:58:32 2005 +0000 @@ -120,6 +120,15 @@ 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 @@ } +/* 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 @@ 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 +};