/* * DVD Player plugin for VDR * * Copyright (C) 2001.2002 Andreas Schultz * * This code is distributed under the terms and conditions of the * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. * */ #include #include #ifndef __QNXNTO__ #include #elif defined(__QNXNTO__) #include #include #include #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 "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; } }