root/modules/ogg/vorbis_dec.c

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

DEFINITIONS

This source file includes following definitions.
  1. VORB_AttachStream
  2. VORB_DetachStream
  3. VORB_GetCapabilities
  4. VORB_SetCapabilities
  5. vorbis_to_intern
  6. VORB_ProcessData
  7. VORB_GetCodecName
  8. NewVorbisDecoder
  9. DeleteVorbisDecoder

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / XIPH.org 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 "ogg_in.h"

#ifdef GPAC_HAS_VORBIS

#include <vorbis/codec.h>

typedef struct
{
        vorbis_info vi;
        vorbis_dsp_state vd;
        vorbis_block vb;
        vorbis_comment vc;
        ogg_packet op;

        u16 ES_ID;
        Bool has_reconfigured;
} VorbDec;

#define VORBISCTX() VorbDec *ctx = (VorbDec *) ((OGGWraper *)ifcg->privateStack)->opaque

static GF_Err VORB_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd)
{
        ogg_packet oggpacket;
        GF_BitStream *bs;

        VORBISCTX();
        if (ctx->ES_ID) return GF_BAD_PARAM;

        if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM;
        if (esd->decoderConfig->objectTypeIndication != GPAC_OTI_MEDIA_OGG) return GF_NON_COMPLIANT_BITSTREAM;
        if ((esd->decoderConfig->decoderSpecificInfo->dataLength<9) || strncmp(&esd->decoderConfig->decoderSpecificInfo->data[3], "vorbis", 6)) return GF_NON_COMPLIANT_BITSTREAM;

        ctx->ES_ID = esd->ESID;

        vorbis_info_init(&ctx->vi);
        vorbis_comment_init(&ctx->vc);

        oggpacket.granulepos = -1;
        oggpacket.b_o_s = 1;
        oggpacket.e_o_s = 0;
        oggpacket.packetno = 0;

        bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ);
        while (gf_bs_available(bs)) {
                oggpacket.bytes = gf_bs_read_u16(bs);
                oggpacket.packet = gf_malloc(sizeof(char) * oggpacket.bytes);
                gf_bs_read_data(bs, oggpacket.packet, oggpacket.bytes);
                if (vorbis_synthesis_headerin(&ctx->vi, &ctx->vc, &oggpacket) < 0 ) {
                        gf_free(oggpacket.packet);
                        gf_bs_del(bs);
                        return GF_NON_COMPLIANT_BITSTREAM;
                }
                gf_free(oggpacket.packet);
        }
        vorbis_synthesis_init(&ctx->vd, &ctx->vi);
        vorbis_block_init(&ctx->vd, &ctx->vb);
        gf_bs_del(bs);

        return GF_OK;
}

static GF_Err VORB_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID)
{
        VORBISCTX();
        if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM;

        vorbis_block_clear(&ctx->vb);
        vorbis_dsp_clear(&ctx->vd);
        vorbis_info_clear(&ctx->vi);
        vorbis_comment_clear(&ctx->vc);

        ctx->ES_ID = 0;
        return GF_OK;
}
static GF_Err VORB_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability)
{
        VORBISCTX();
        switch (capability->CapCode) {
        case GF_CODEC_RESILIENT:
                capability->cap.valueInt = 1;
                break;
        case GF_CODEC_OUTPUT_SIZE:
                capability->cap.valueInt = vorbis_info_blocksize(&ctx->vi, 1) * 2 * ctx->vi.channels;
                break;
        case GF_CODEC_SAMPLERATE:
                capability->cap.valueInt = ctx->vi.rate;
                break;
        case GF_CODEC_NB_CHAN:
                capability->cap.valueInt = ctx->vi.channels;
                break;
        case GF_CODEC_BITS_PER_SAMPLE:
                capability->cap.valueInt = 16;
                break;
        case GF_CODEC_BUFFER_MIN:
                capability->cap.valueInt = 4;
                break;
        case GF_CODEC_BUFFER_MAX:
                capability->cap.valueInt = ctx->vi.rate / 4 / vorbis_info_blocksize(&ctx->vi, 0);
                /*blocks are not of fixed size, so indicate a default CM size*/
                //capability->cap.valueInt = 12 * vorbis_info_blocksize(&ctx->vi, 1) / vorbis_info_blocksize(&ctx->vi, 0);
                break;
        case GF_CODEC_CU_DURATION:
                /*this CANNOT work with vorbis, blocks are not of fixed size*/
                capability->cap.valueInt = 0;
                break;
        case GF_CODEC_PADDING_BYTES:
                capability->cap.valueInt = 0;
                break;
        case GF_CODEC_CHANNEL_CONFIG:
                switch (ctx->vi.channels) {
                case 1:
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER;
                        break;
                case 2:
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT;
                        break;
                case 3:
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_FRONT_CENTER;
                        break;
                case 4:
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT;
                        break;
                case 5:
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_FRONT_CENTER | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT;
                        break;
                case 6:
                        capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_FRONT_CENTER | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT | GF_AUDIO_CH_LFE;
                        break;
                }
                break;
        default:
                capability->cap.valueInt = 0;
                break;
        }
        return GF_OK;
}

static GF_Err VORB_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability)
{
        /*return unsupported to avoid confusion by the player (like SR changing ...) */
        return GF_NOT_SUPPORTED;
}


static GFINLINE void vorbis_to_intern(u32 samples, Float **pcm, char *buf, u32 channels)
{
        u32 i, j;
        s32 val;
        ogg_int16_t *ptr, *data = (ogg_int16_t*)buf ;
        Float *mono;

        for (i=0 ; i<channels ; i++) {
                ptr = &data[i];
                if (channels>2) {
                        /*center is third in gpac*/
                        if (i==1) ptr = &data[2];
                        /*right is 2nd in gpac*/
                        else if (i==2) ptr = &data[1];
                        /*LFE is 4th in gpac*/
                        if ((channels==6) && (i>3)) {
                                if (i==6) ptr = &data[4];       /*LFE*/
                                else ptr = &data[i+1];  /*back l/r*/
                        }
                }

                mono = pcm[i];
                for (j=0; j<samples; j++) {
                        val = (s32) (mono[j] * 32767.f);
                        if (val > 32767) val = 32767;
                        if (val < -32768) val = -32768;
                        *ptr = val;
                        ptr += channels;
                }
        }
}

static GF_Err VORB_ProcessData(GF_MediaDecoder *ifcg,
                               char *inBuffer, u32 inBufferLength,
                               u16 ES_ID, u32 *CTS,
                               char *outBuffer, u32 *outBufferLength,
                               u8 PaddingBits, u32 mmlevel)
{
        ogg_packet op;
        Float **pcm;
        u32 samples, total_samples, total_bytes;

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

        op.granulepos = -1;
        op.b_o_s = 0;
        op.e_o_s = 0;
        op.packetno = 0;
        op.packet = inBuffer;
        op.bytes = inBufferLength;


        *outBufferLength = 0;

        if (vorbis_synthesis(&ctx->vb, &op) == 0)
                vorbis_synthesis_blockin(&ctx->vd, &ctx->vb) ;

        /*trust vorbis max block info*/
        total_samples = 0;
        total_bytes = 0;
        while ((samples = vorbis_synthesis_pcmout(&ctx->vd, &pcm)) > 0) {
                vorbis_to_intern(samples, pcm, (char*) outBuffer + total_bytes, ctx->vi.channels);
                total_bytes += samples * 2 * ctx->vi.channels;
                total_samples += samples;
                vorbis_synthesis_read(&ctx->vd, samples);
        }
        *outBufferLength = total_bytes;
        return GF_OK;
}


static const char *VORB_GetCodecName(GF_BaseDecoder *dec)
{
        return "Vorbis Decoder";
}

u32 NewVorbisDecoder(GF_BaseDecoder *ifcd)
{
        VorbDec *dec;
        GF_SAFEALLOC(dec, VorbDec);
        ((OGGWraper *)ifcd->privateStack)->opaque = dec;
        ((OGGWraper *)ifcd->privateStack)->type = OGG_VORBIS;
        /*setup our own interface*/
        ifcd->AttachStream = VORB_AttachStream;
        ifcd->DetachStream = VORB_DetachStream;
        ifcd->GetCapabilities = VORB_GetCapabilities;
        ifcd->SetCapabilities = VORB_SetCapabilities;
        ((GF_MediaDecoder*)ifcd)->ProcessData = VORB_ProcessData;
        ifcd->GetName = VORB_GetCodecName;
        return 1;
}

void DeleteVorbisDecoder(GF_BaseDecoder *ifcg)
{
        VorbDec *ctx;
        if (!ifcg || !ifcg->privateStack)
                return;
        ctx = (VorbDec *) ((OGGWraper *)ifcg->privateStack)->opaque;
        if (ctx) {
                gf_free(ctx);
                ((OGGWraper *)ifcg->privateStack)->opaque = NULL;
        }
}

#endif


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