Logo Search packages:      
Sourcecode: vdr-plugin-dvd version File versions  Download package

ca52.c

/*
 * DVD Player plugin for VDR
 *
 * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 */

#include <stdio.h>
#include <memory.h>

#ifndef __QNXNTO__

#include <byteswap.h>

#elif defined(__QNXNTO__)

#include <gulliver.h>
#include <stdint.h>
#include <strings.h>

#ifdef WORDS_BIGENDIAN
#define bswap_16(x) ENDIAN_LE16(x)
#define bswap_32(x) ENDIAN_LE32(x)
#define bswap_64(x) ENDIAN_LE64(x)
#else
#define bswap_16(x) ENDIAN_BE16(x)
#define bswap_32(x) ENDIAN_BE32(x)
#define bswap_64(x) ENDIAN_BE64(x)
#endif

#endif

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "dvddev.h"
#include "mpegtypes.h"
#include "setup-dvd.h"
#include "tools-dvd.h"
#include "player-dvd.h"
#include "ca52.h"

// #define A52DEBUG

#ifdef A52DEBUG
#warning A52DEBUG is defined
#define DEBUG_A52(format, args...) fprintf (stderr, format, ## args)
#else
#define DEBUG_A52(format, args...)
#endif

static const uint8_t burstHeader[4] = { 0xf8, 0x72, 0x4e, 0x1f };

A52decoder::A52decoder(cDvdPlayer &ThePlayer): player(ThePlayer)
{
  syncMode = ptsCopy;
  apts = 0;
  state = a52_init (MM_ACCEL_DJBFFT);
//  state = a52_init(MM_ACCEL_X86_MMXEXT);

  blk_buf = (uchar *)malloc(25*1024);
  blk_ptr = blk_buf;
  blk_size = 0;

  setup();
}

A52decoder::~A52decoder()
{
  if(blk_buf) free(blk_buf) ; blk_buf=NULL;
}

void A52decoder::setup(void)
{
  flags = A52_DOLBY;

  level = DVDSetup.Gain;
  bias = 384;
}

#define convert(x) x>0x43c07fff ? bswap_16(0x7fff) : x < 0x43bf8000 ? bswap_16(0x8000) : bswap_16(x - 0x43c00000);

#if 0
static inline int16_t bswap(int16_t i)
{
  return bswap_16(i);
}

inline int16_t A52decoder::convert (int32_t i)
{
    if (i > 0x43c07fff)
      return bswap(32767);
    else if (i < 0x43bf8000)
      return bswap(-32768);
    else
      return bswap(i - 0x43c00000);
}
#endif

inline void A52decoder::float_to_int (float * _f, int16_t * s16, int flags)
{
    int i;
    int32_t * f = (int32_t *) _f;

    switch (flags) {
        case A52_MONO: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = s16[ic+1] = s16[ic+2] = s16[ic+3] = 0;
                s16[ic+4] = convert (f[i]);
                ic+=5;
            }
            break;
        }
        case A52_CHANNEL:
        case A52_STEREO:
        case A52_DOLBY: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i]);
                s16[ic+1] = convert (f[i+256]);
                ic +=2;
            }
            break;
        }
        case A52_3F: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i]);
                s16[ic+1] = convert (f[i+512]);
                s16[ic+2] = s16[ic+3] = 0;
                s16[ic+4] = convert (f[i+256]);
                ic +=5;
            }
            break;
        }
        case A52_2F2R: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i]);
                s16[ic+1] = convert (f[i+256]);
                s16[ic+2] = convert (f[i+512]);
                s16[ic+3] = convert (f[i+768]);
                ic +=4;
            }
            break;
        }
        case A52_3F2R: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i]);
                s16[ic+1] = convert (f[i+256]);
                s16[ic+2] = convert (f[i+512]);
                s16[ic+3] = convert (f[i+768]);
                s16[ic+4] = convert (f[i+1024]);
                ic +=5;
            }
            break;
        }
        case A52_MONO | A52_LFE: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = s16[ic+1] = s16[ic+2] = s16[ic+3] = 0;
                s16[ic+4] = convert (f[i+256]);
                s16[ic+5] = convert (f[i]);
                ic +=6;
            }
            break;
        }
        case A52_CHANNEL | A52_LFE:
        case A52_STEREO | A52_LFE:
        case A52_DOLBY | A52_LFE: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i+256]);
                s16[ic+1] = convert (f[i+512]);
                s16[ic+2] = s16[ic+3] = s16[ic+4] = 0;
                s16[ic+5] = convert (f[i]);
                ic +=6;
            }
            break;
        }
        case A52_3F | A52_LFE: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i+256]);
                s16[ic+1] = convert (f[i+768]);
                s16[ic+2] = s16[ic+3] = 0;
                s16[ic+4] = convert (f[i+512]);
                s16[ic+5] = convert (f[i]);
                ic +=6;
            }
            break;
        }
        case A52_2F2R | A52_LFE: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i+256]);
                s16[ic+1] = convert (f[i+512]);
                s16[ic+2] = convert (f[i+768]);
                s16[ic+3] = convert (f[i+1024]);
                s16[ic+4] = 0;
                s16[ic+5] = convert (f[i]);
                ic +=6;
            }
            break;
        }
        case A52_3F2R | A52_LFE: {
            int ic=0;
            for (i = 0; i < 256; i++) {
                s16[ic] = convert (f[i+256]);
                s16[ic+1] = convert (f[i+768]);
                s16[ic+2] = convert (f[i+1024]);
                s16[ic+3] = convert (f[i+1280]);
                s16[ic+4] = convert (f[i+512]);
                s16[ic+5] = convert (f[i]);
                ic +=6;
            }
            break;
        }
    }

}

// data=PCM samples, 16 bit, LSB first, 48kHz, stereo
void A52decoder::init_ipack(int p_size, uint32_t pktpts, uint8_t SubStreamId)
{
    int header = pktpts ? 5 :0;

    int length = 10 + header + p_size;

    blk_ptr[0] = 0x00;
    blk_ptr[1] = 0x00;
    blk_ptr[2] = 0x01;
    blk_ptr[3] = PRIVATE_STREAM1;
    blk_ptr[4] = (length >> 8) & 0xff;
    blk_ptr[5] = length & 0xff;
    blk_ptr[6] = 0x84;
    blk_ptr[7] = pktpts ? 0x80 : 0;
    blk_ptr[8] = header;
    blk_ptr += 9;

    if (header) {
        cPStream::toPTS(blk_ptr, pktpts);
    }

    blk_ptr += header;

/**
 *
struct LPCMHeader { int id:8;              // id
                    int frame_count:8;     // number of frames
                    int access_ptr:16;     // first acces unit pointer, i.e. start of audio frame
                    bool emphasis:1;       // audio emphasis on-off
                    bool mute:1;           // audio mute on-off
                    bool reserved:1;       // reserved
                    int frame_number:5;    // audio frame number
                    int quant_wlen:2;      // quantization word length
                    int sample_freq:2;     // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3)
                    bool reserved2:1;      // reserved
                    int chan_count:3;      // number of audio channels - 1 (e.g. stereo = 1)
                    int dyn_range_ctrl:8;  // dynamic range control (0x80 if off)
                    };
 *
 */
    blk_ptr[0] = aLPCM + SubStreamId; // substream ID
    // other stuff (see DVB specs), may be ignored by driver
    // but try to set it up correctly ..
    blk_ptr[1] = 0x07;  // number of frames
    blk_ptr[2] = 0x00;  // start of audio frame
    blk_ptr[3] = 0x04;  // start of audio frame
    blk_ptr[4] = 0x0C;  // emph, mute, res, frame_num
    blk_ptr[5] = 0x01;  // quantwlen, samplefreq, channels (48kHz, stereo)
    blk_ptr[6] = 0x80;  // dynam. range ctrl (off)
    blk_ptr += 7;

    blk_size += 16 + header;

}


int A52decoder::convertSample(int flags, a52_state_t * _state,    uint32_t pktpts, uint8_t SubStreamId) {
    int chans = -1;
    sample_t * _samples = a52_samples(_state);

#ifdef LIBA52_DOUBLE
    float samples[256 * 6];
    int i;

    for (i = 0; i < 256 * 6; i++)
      samples[i] = _samples[i];
#else
    float * samples = _samples;
#endif

    flags &= A52_CHANNEL_MASK | A52_LFE;

    if (flags & A52_LFE)
      chans = 6;
    else if (flags & 1) /* center channel */
      chans = 5;
    else switch (flags) {
    case A52_2F2R:
      chans = 4;
      break;
    case A52_CHANNEL:
    case A52_STEREO:
    case A52_DOLBY:
      chans = 2;
      break;
    default:
      return 1;
    }

    init_ipack((int) 256 * sizeof (int16_t) * chans, pktpts, SubStreamId);
    float_to_int (samples, (int16_t *)blk_ptr, flags);
    // push data to driver
    blk_ptr  += (int) 256 * sizeof (int16_t) * chans;
    blk_size += (int) 256 * sizeof (int16_t) * chans;
    return 0;
}

void A52decoder::clear()
{
  DEBUG_A52("A52decoder::clear()\n");
  blk_ptr = blk_buf;
  blk_size = 0;
  apts = 0;
  a52asm.clear();
}

/* from a52a.pdf:
 *
 * The AC-3 audio access unit (AU) or presentation unit (PU)
 * is an AC-3 sync frame. The AC-3 sync frame contains 1 536 audio
 * samples. The duration of an AC-3 access (or presentation) unit
 * is 32 ms for audio sampled at 48 kHz, approximately 34.83 ms for
 * audio sampled at 44.1 kHz, and 48 ms for audio sampled at 32 kHz.
 *
 * in 90kHz pts cycles:
 *   48kHz   = 2880
 *   44.1kHz = 3134.69
 *   32kHz   = 4320
 *
 * for now, lets assume we always have 48kHz!!
 */

void A52decoder::decode(uint8_t * start, int size, uint32_t pktpts, uint8_t SubStreamId)
{
    int bit_rate;

    //do we enter with an empty buffer
    bool sendPTS = a52asm.used() < 40;

    DEBUG_A52("pts: %d %8d %8d, %8d\n",
        sendPTS, apts, pktpts, apts - pktpts);

    if (a52asm.used() == 0 || apts == 0 ||
      apts > (pktpts + 2880) || (apts + 5760) < pktpts) {
          DEBUG_A52("reseting Audio PTS\n");
          apts = pktpts;
    }

    blk_ptr = blk_buf;
    blk_size = 0;

    while (size) {

      int res = a52asm.put(start, size, pktpts);
      start += res;
      size  -= res;
    if ( res==0 ) {
        clear();
        break;
    }
      if (a52asm.ready()) {

          int length;
          A52frame *frm = a52asm.get();
          if (!frm)
            goto error;
          length = a52_syncinfo (frm->frame,
                           &flags, &sample_rate, &bit_rate);
          if (!length) {
            delete frm;
            continue;
          }
          if (syncMode == ptsGenerate) {
            pktpts  = apts;
            sendPTS = true;
          }

          int i;

          setup();
          flags |= A52_ADJUST_LEVEL;
          if (a52_frame (state, frm->frame, &flags, &level, bias))
            goto error;
          if (!DVDSetup.AC3dynrng)
            a52_dynrng (state, NULL, NULL);
          for (i = 0; i < 6; i++) {
            if (a52_block (state))
                goto error;
            if (sendPTS) {
                player.seenAPTS(pktpts);
                DEBUG_A52("sending PTS\n");
            }
            if (convertSample(flags, state, sendPTS ? pktpts : 0, SubStreamId))
                goto error;
            pktpts  = 0;
            sendPTS = false;
          }
          apts += 2880;
          delete frm;
          continue;
      error:
          DEBUG_A52("error\n");
          sendPTS = false;
          apts = 0;
          if (frm)
            delete frm;
      }
    }
    if (blk_size > 0)
      player.cbPlayAudio(blk_buf, blk_size);
}

A52frame::A52frame(int datasize, int frmsize, uint32_t apts)
{
    frame = new uint8_t[datasize+2];
    frame[0] = 0x0b;
    frame[1] = 0x77;
    size = frmsize;
    pos  = 2;
    pts  = apts;
}

A52frame::~A52frame()
{
    delete[] frame;
}

A52assembler::A52assembler()
{
    curfrm = NULL;
    syncword = 0xffff;
}

A52assembler::~A52assembler()
{
    if (curfrm)
      delete curfrm;
}

void A52assembler::clear(void)
{
    if (curfrm)
      delete curfrm;
    curfrm = NULL;
    syncword = 0xffff;
}

A52frame *A52assembler::get(void)
{
    A52frame *frm = curfrm;
    curfrm = NULL;
    syncword = 0xffff;

    return frm;
}

int A52assembler::used(void)
{
    if (curfrm) return curfrm->pos;
    return 0;
}

bool A52assembler::ready(void)
{
    return curfrm && curfrm->pos == curfrm->size;
};

#ifndef WORDS_BIGENDIAN
# define char2short(a,b)        ((((b) << 8) & 0xffff) ^ ((a) & 0xffff))
# define shorts(a)              (a)
#else
# define char2short(a,b)        ((((a) << 8) & 0xffff) ^ ((b) & 0xffff))
# define shorts(a)              char2short(((a) & 0xff),(((a) >> 8) & 0xff));
#endif

int A52assembler::put(uint8_t *buf, int len, uint32_t pts)
{
    if (ready())
      return 0;

    uint8_t *data = buf;
    uint8_t *end = data + len;

    while(syncword != char2short(0x77, 0x0b)) {
          if (data >= end) {
              DEBUG_A52("skipping %d bytes without finding anything\n", data - buf);
              return data - buf;
          }
        syncword = (syncword << 8) | *data++;
    }

    int frmsize  = 0;
    if (!curfrm) {
          int datasize = 1920;
          if (end - data > 3)
              datasize = frmsize = parse_syncinfo(data);
          curfrm = new A52frame(datasize, frmsize, pts);
    }
    if (curfrm->size == 0) {
          if (curfrm->pos < 6) {
              frmsize = 6 - curfrm->pos;
          } else {
              curfrm->size = parse_syncinfo(curfrm->frame+2);
              frmsize = curfrm->size - curfrm->pos;
          }
    } else
          frmsize = curfrm->size - curfrm->pos;

    if (frmsize > end - data)
          frmsize = end - data;
    if (frmsize<0) return 0;
    memcpy(curfrm->frame + curfrm->pos, data, frmsize);
    curfrm->pos += frmsize;

    return data + frmsize - buf;
}

// Parse a syncinfo structure, minus the sync word
int A52assembler::parse_syncinfo(uint8_t *data)
{
    static int rate[] = { 32,  40,  48,  56,  64,  80,  96, 112,
                         128, 160, 192, 224, 256, 320, 384, 448,
                         512, 576, 640};
    int frmsizecod;
    int bitrate;

    if (data[3] >= 0x60)         /* bsid >= 12 */
        return 0;

    frmsizecod = data[2] & 63;
    if (frmsizecod >= 38)
        return 0;
    bitrate = rate [frmsizecod >> 1];

    switch (data[2] & 0xc0) {
    case 0:
        return 4 * bitrate;
    case 0x40:
        return 2 * (320 * bitrate / 147 + (frmsizecod & 1));
    case 0x80:
        return 6 * bitrate;
    default:
        return 0;
    }
}


Generated by  Doxygen 1.6.0   Back to index