root/modules/mp3_in/mad_dec.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MAD_AttachStream
  2. MAD_DetachStream
  3. MAD_GetCapabilities
  4. MAD_SetCapabilities
  5. MAD_ProcessData
  6. MAD_GetCodecName
  7. MAD_CanHandleStream
  8. NewMADDec
  9. DeleteMADDec

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2005-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / codec pack module
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *
 *  GPAC 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 this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <gpac/setup.h>

#ifdef GPAC_HAS_MAD

#include <gpac/modules/codec.h>
#include <gpac/constants.h>

#if defined(_WIN32_WCE) || defined(_WIN64) || defined(__SYMBIAN32__)
#ifndef FPM_DEFAULT
#define FPM_DEFAULT
#endif
#endif

#include <mad.h>

#if !defined(__GNUC__)
# if defined(_WIN32_WCE) || defined (WIN32)
#  pragma comment(lib, "libmad")
# endif
#endif


typedef struct
{
        Bool configured;

        u32 sample_rate, out_size, num_samples;
        u8 num_channels;
        /*no support for scalability in FAAD yet*/
        u16 ES_ID;
        u32 cb_size, cb_trig;

        unsigned char *buffer;
        u32 len;
        Bool first;


        struct mad_frame frame;
        struct mad_stream stream;
        struct mad_synth synth;

} MADDec;

#define MADCTX() MADDec *ctx = NULL; if (ifcg) { ctx = (MADDec *) ifcg->privateStack; }

static GF_Err MAD_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd)
{
        MADCTX();
        assert( ctx );
        if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED;

        if (ctx->configured) {
                mad_stream_finish(&ctx->stream);
                mad_frame_finish(&ctx->frame);
                mad_synth_finish(&ctx->synth);
        }
        mad_stream_init(&ctx->stream);
        mad_frame_init(&ctx->frame);
        mad_synth_init(&ctx->synth);
        ctx->configured = GF_TRUE;

        ctx->buffer = (unsigned char*)gf_malloc(sizeof(char) * 2*MAD_BUFFER_MDLEN);

        /*we need a frame to init, so use default values*/
        ctx->num_samples = 1152;
        ctx->num_channels = 0;
        ctx->sample_rate = 0;
        ctx->out_size = 2 * ctx->num_samples * ctx->num_channels;
        ctx->ES_ID = esd->ESID;
        ctx->first = GF_TRUE;
        return GF_OK;
}

static GF_Err MAD_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID)
{
        MADCTX();
        assert( ctx );
        if (ES_ID != ctx->ES_ID) return GF_BAD_PARAM;
        assert( ifcg );
        assert( ctx );
        ctx->ES_ID = 0;
        if (ctx->buffer) gf_free(ctx->buffer);
        ctx->buffer = NULL;
        ctx->sample_rate = ctx->out_size = ctx->num_samples = 0;
        ctx->num_channels = 0;
        if (ctx->configured) {
                mad_stream_finish(&ctx->stream);
                mad_frame_finish(&ctx->frame);
                mad_synth_finish(&ctx->synth);
        }
        ctx->configured = GF_FALSE;
        return GF_OK;
}
static GF_Err MAD_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability)
{
        MADCTX();
        assert( ctx );
        switch (capability->CapCode) {
        /*not tested yet*/
        case GF_CODEC_RESILIENT:
                capability->cap.valueInt = 1;
                break;
        case GF_CODEC_OUTPUT_SIZE:
                capability->cap.valueInt = ctx->out_size;
                break;
        case GF_CODEC_SAMPLERATE:
                capability->cap.valueInt = ctx->sample_rate;
                break;
        case GF_CODEC_NB_CHAN:
                capability->cap.valueInt = ctx->num_channels;
                break;
        case GF_CODEC_BITS_PER_SAMPLE:
                capability->cap.valueInt = 16;
                break;
        case GF_CODEC_BUFFER_MIN:
                capability->cap.valueInt = ctx->cb_trig;
                break;
        case GF_CODEC_BUFFER_MAX:
                capability->cap.valueInt = ctx->cb_size;
                break;
        /*FIXME: get exact sampling window*/
        case GF_CODEC_CU_DURATION:
                capability->cap.valueInt = ctx->num_samples;
                break;
        case GF_CODEC_PADDING_BYTES:
                capability->cap.valueInt = 0;
                break;
        case GF_CODEC_CHANNEL_CONFIG:
                if (ctx->num_channels==1) {
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER;
                } else {
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT;
                }
                break;
        default:
                capability->cap.valueInt = 0;
                break;
        }
        return GF_OK;
}

static GF_Err MAD_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability)
{
        MADCTX();
        assert( ctx );
        switch (capability.CapCode) {
        /*reset storage buffer*/
        case GF_CODEC_WAIT_RAP:
                ctx->first = GF_TRUE;
                ctx->len = 0;
                if (ctx->configured) {
                        mad_stream_finish(&ctx->stream);
                        mad_frame_finish(&ctx->frame);
                        mad_synth_finish(&ctx->synth);

                        mad_stream_finish(&ctx->stream);
                        mad_frame_finish(&ctx->frame);
                        mad_synth_finish(&ctx->synth);
                }
                return GF_OK;
        default:
                /*return unsupported to avoid confusion by the player (like SR changing ...) */
                return GF_NOT_SUPPORTED;
        }
}

/*from miniMad.c*/
#define MAD_SCALE(ret, s_chan)  \
        chan = s_chan;                          \
        chan += (1L << (MAD_F_FRACBITS - 16));          \
        if (chan >= MAD_F_ONE)                                  \
                chan = MAD_F_ONE - 1;                                   \
        else if (chan < -MAD_F_ONE)                             \
                chan = -MAD_F_ONE;                              \
        ret = chan >> (MAD_F_FRACBITS + 1 - 16);                \
 
static GF_Err MAD_ProcessData(GF_MediaDecoder *ifcg,
                              char *inBuffer, u32 inBufferLength,
                              u16 ES_ID, u32 *CTS,
                              char *outBuffer, u32 *outBufferLength,
                              u8 PaddingBits, u32 mmlevel)
{
        mad_fixed_t *left_ch, *right_ch, chan;
        char *ptr;
        u32 num, outSize;
        MADCTX();
        assert( ctx );

        /*check not using scalabilty*/
        assert(ctx->ES_ID == ES_ID);

        if (ctx->ES_ID != ES_ID)
                return GF_BAD_PARAM;

        /*if late or seeking don't decode*/
        switch (mmlevel) {
        case GF_CODEC_LEVEL_SEEK:
                *outBufferLength = 0;
                return GF_OK;
        default:
                break;
        }

        if (ctx->out_size > *outBufferLength) {
                *outBufferLength = ctx->out_size;
                return GF_BUFFER_TOO_SMALL;
        }

        if (ctx->first) {
                ctx->first = GF_FALSE;
                memcpy(ctx->buffer, inBuffer, inBufferLength);
                ctx->len = inBufferLength;
                *outBufferLength = 0;
                return GF_OK;
        }
        memcpy(ctx->buffer + ctx->len, inBuffer, inBufferLength);
        ctx->len += inBufferLength;
        mad_stream_buffer(&ctx->stream, ctx->buffer, ctx->len);

        if (mad_frame_decode(&ctx->frame, &ctx->stream) == -1) {
                memcpy(ctx->buffer, inBuffer, inBufferLength);
                ctx->len = inBufferLength;
                *outBufferLength = 0;
                return GF_NON_COMPLIANT_BITSTREAM;
        }


        /*first cfg, reconfig composition buffer*/
        if (!ctx->sample_rate) {
                mad_synth_frame(&ctx->synth, &ctx->frame);
                ctx->len -= inBufferLength;
                ctx->sample_rate = ctx->synth.pcm.samplerate;
                ctx->num_channels = (u8) ctx->synth.pcm.channels;
                ctx->num_samples = ctx->synth.pcm.length;
                ctx->out_size = 2 * ctx->num_samples * ctx->num_channels;
                *outBufferLength = ctx->out_size;
                GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[MAD] decoder initialized - MP3 sample rate %d - %d channel(s)", ctx->sample_rate, ctx->num_channels));
                return GF_BUFFER_TOO_SMALL;
        }

        if (ctx->stream.next_frame) {
                ctx->len = (u32) (&ctx->buffer[ctx->len] - ctx->stream.next_frame);
                memmove(ctx->buffer, ctx->stream.next_frame, ctx->len);
        }


        mad_synth_frame(&ctx->synth, &ctx->frame);
        num = ctx->synth.pcm.length;
        ptr = (char *) outBuffer;
        left_ch = ctx->synth.pcm.samples[0];
        right_ch = ctx->synth.pcm.samples[1];
        outSize = 0;

        while (num--) {
                s32 rs;
                MAD_SCALE(rs, (*left_ch++) );

                *ptr = (rs >> 0) & 0xff;
                ptr++;
                *ptr = (rs >> 8) & 0xff;
                ptr++;
                outSize += 2;

                if (ctx->num_channels == 2) {
                        MAD_SCALE(rs, (*right_ch++) );
                        *ptr = (rs >> 0) & 0xff;
                        ptr++;
                        *ptr = (rs >> 8) & 0xff;
                        ptr++;
                        outSize += 2;
                }
        }
        *outBufferLength = outSize;
        return GF_OK;
}

static const char *MAD_GetCodecName(GF_BaseDecoder *dec)
{
        return "MAD " \
               MAD_VERSION;
}

static u32 MAD_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, GF_ESD *esd, u8 PL)
{
        /*audio decs*/
        if (StreamType != GF_STREAM_AUDIO) return GF_CODEC_NOT_SUPPORTED;
        /*media type query*/
        if (!esd) return GF_CODEC_STREAM_TYPE_SUPPORTED;

        switch (esd->decoderConfig->objectTypeIndication) {
        /*MPEG1 audio*/
        case GPAC_OTI_AUDIO_MPEG2_PART3:
        /*MPEG2 audio*/
        case GPAC_OTI_AUDIO_MPEG1:
                return GF_CODEC_SUPPORTED;
        }
        return GF_CODEC_NOT_SUPPORTED;
}

GF_BaseDecoder *NewMADDec()
{
        GF_MediaDecoder *ifce;
        MADDec *dec;

        GF_SAFEALLOC(ifce, GF_MediaDecoder);
        if (!ifce) return NULL;
        GF_SAFEALLOC(dec, MADDec);
        if (!dec) {
                gf_free(ifce);
                return NULL;
        }
        GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "MAD Decoder", "gpac distribution")
        ifce->privateStack = dec;

        dec->cb_size = 12;
        dec->cb_trig = 4;

        /*setup our own interface*/
        ifce->AttachStream = MAD_AttachStream;
        ifce->DetachStream = MAD_DetachStream;
        ifce->GetCapabilities = MAD_GetCapabilities;
        ifce->SetCapabilities = MAD_SetCapabilities;
        ifce->GetName = MAD_GetCodecName;
        ifce->ProcessData = MAD_ProcessData;
        ifce->CanHandleStream = MAD_CanHandleStream;
        return (GF_BaseDecoder *)ifce;
}

void DeleteMADDec(GF_MediaDecoder *ifcg)
{
        MADDec *ctx;
        if (!ifcg)
                return;
        ctx = (MADDec *) ifcg->privateStack;
        ifcg->privateStack = NULL;
        if (ctx) {
                if (ctx->configured) {
                        mad_stream_finish(&ctx->stream);
                        mad_frame_finish(&ctx->frame);
                        mad_synth_finish(&ctx->synth);
                }
                ctx->configured = GF_FALSE;
                ctx->sample_rate = ctx->out_size = ctx->num_samples = 0;
                ctx->num_channels = 0;
                gf_free(ctx);
        }
        gf_free(ifcg);
}

#endif


/* [<][>][^][v][top][bottom][index][help] */