diff options
author | austriancoder <austriancoder> | 2005-08-15 17:28:49 +0000 |
---|---|---|
committer | austriancoder <austriancoder> | 2005-08-15 17:28:49 +0000 |
commit | 290038f3de13b728cab6d3db7729078482903844 (patch) | |
tree | b83287be6ad0602f7f73a91c11b36074e5062be0 /dxr3multichannelaudio.c | |
parent | 8955b4dfa419698ecb853bb8f1cdc1fe96d206d6 (diff) | |
download | vdr-plugin-dxr3-290038f3de13b728cab6d3db7729078482903844.tar.gz vdr-plugin-dxr3-290038f3de13b728cab6d3db7729078482903844.tar.bz2 |
HEAD = 0.2.3 release, because i want to start my coding with this codebase
Diffstat (limited to 'dxr3multichannelaudio.c')
-rw-r--r-- | dxr3multichannelaudio.c | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/dxr3multichannelaudio.c b/dxr3multichannelaudio.c new file mode 100644 index 0000000..abab4b8 --- /dev/null +++ b/dxr3multichannelaudio.c @@ -0,0 +1,784 @@ +/* + * dxr3multichannelaudio.c: + * + * taken from the AC3overDVB Patch maintained by Stefan Huelswitt + * + * + */ + +#include <malloc.h> +#include "dxr3multichannelaudio.h" +#include <vdr/ringbuffer.h> + +//#define DEBUG(x...) printf(x) +#define DEBUG(x...) + +//#define ED(x...) printf(x) +#define ED(x...) + +#define aAC3 0x80 +#define aDTS 0x88 +#define aLPCM 0xA0 +#define aMPEG 0xC0 + +#define aVDR 0x0B // VDR specific audio substream +#define aSPU 0x20 // SPU stream + +#define PES_HDR_SIZE 6 // length of PES header +#define PTS_SIZE 5 // length of PTS data +#define MAX_FRAMECOUNT 1536 // max. LPCM payload size + +#define SYNC_SIZE 7 // how many bytes needed to sync on a audio header + +#define AC3_SIZE 6144 // size of AC3 IEC paket +#define DTS_SIZE 2048 // size of DTS IEC paket +#define IEC_HDR_SIZE 8 // size of IEC header + +// --- cAudioEncapsulator ----------------------------------------------------- + +class cAudioEncapsulator +{ +private: + int totalSize, frameCount; + cFrame *frame; + uchar *frameData; + // + uchar syncBuff[SYNC_SIZE]; + int have, length, skipped; + // + uchar ptsFlags; + const uchar *ptsData; + int ptsDelay; + // + void NewFrame(uchar PTSflags, const uchar *PTSdata); + void SyncFound(const uchar *data); +protected: + int streamType; + cRingBufferFrame *ringBuffer; + int fillup, firstBurst; + bool mute, muteData; + // + void StartFrame(int size, uchar PTSflags, const uchar *PTSdata); + void FinishFrame(void); + void PutData(const uchar *data, int len); + void SendIECpause(int type, uchar PTSflags, const uchar *PTSdata); + // + virtual int SyncInfo(const uchar *data)=0; + virtual void StartIECFrame(const uchar *buf, int length, uchar PTSflags, + const uchar *PTSdata) = 0; + virtual void FinishIECFrame(void); +public: + cAudioEncapsulator(cRingBufferFrame *rb, int StreamType); + virtual ~cAudioEncapsulator(); + void Clear(void); + void Decode(const uchar *data, int len, uchar PTSflags, int PTSdelay, + const uchar *PTSdata); + int StreamType() + { + return streamType; + } + void Mute(bool Mute) + { + mute = Mute; + } +}; + +cAudioEncapsulator::cAudioEncapsulator(cRingBufferFrame *rb, int StreamType) +{ + ringBuffer = rb; + streamType = StreamType; + frame = 0; + firstBurst = 1; + Clear(); +} + +cAudioEncapsulator::~cAudioEncapsulator() +{ + delete frame; +} + +void cAudioEncapsulator::Clear(void) +{ + delete frame; + frame = 0; + frameCount = 0; + fillup = 0; + mute = muteData = false; + have = length = skipped = 0; +} + +void cAudioEncapsulator::StartFrame(int size, uchar PTSflags, + const uchar *PTSdata) +{ + if (frame) + { + DEBUG("StartFrame() with unfinished frame!\n"); + FinishFrame(); + } + ED("StartFrame: size=%d ptsFlags=%d\n", size, PTSflags); + totalSize = size; + NewFrame(PTSflags, PTSdata); +} + +void cAudioEncapsulator::NewFrame(uchar PTSflags, const uchar *PTSdata) +{ + if (!totalSize) + { + DEBUG("NewFrame: new frame requested, but totalSize=0\n"); + return; + } + static const int ptslen[] = { 0, 0, PTS_SIZE, PTS_SIZE*2 }; + const int plen = ptslen[PTSflags]; + int len = std::min(totalSize, MAX_FRAMECOUNT); + ED("NewFrame: totalSize=%d frameCount=%d PTSflags=%d", totalSize, len, PTSflags); + totalSize -= len; + ED(" new totalSize=%d\n",totalSize); + len += (plen + 3 + 7); + frameCount = len + PES_HDR_SIZE; + frameData = MALLOC(uchar, frameCount); + if (frameData) + { + frame = new cFrame(frameData, -frameCount, ftUnknown); + if (frame) + { + uchar buf[10]; + // build the PES header + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x01; + buf[3] = 0xBD; // PRIVATE_STREAM1 + buf[4] = (len >> 8) & 0xFF; + buf[5] = len & 0xFF; + buf[6] = 0x84; + buf[7] = plen ? (PTSflags << 6) : 0; + buf[8] = plen; + PutData(buf, 9); + + if (plen) + PutData(PTSdata, plen); + + // build LPCM header + buf[0] = aLPCM; // substream ID + buf[1] = 0xFF; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x81; + PutData(buf, 7); + return; + } + else + { + free(frameData); + frameData = 0; + } + } + esyslog("Failed to build frame for audio encapsulation"); +} + +void cAudioEncapsulator::FinishFrame(void) +{ + if (frameCount) + { + DEBUG("FinishFrame() with frameCount>0\n"); + PutData(0, frameCount); + } + if (frame && frameData) + { + ED("FinishFrame: totalSize=%d\n", totalSize); + if (!ringBuffer->Put(frame)) { + esyslog("Ringbuffer overflow. Encapsulated audio frame lost"); + delete frame; + } + } + frame = 0; + frameData = 0; + frameCount = 0; +} + +void cAudioEncapsulator::PutData(const uchar *data, int len) +{ + if (!muteData) + { + if (!frameData) + DEBUG("PutData() without frame\n"); + while (frameData && len > 0) + { + int l = std::min(len, frameCount); + if (data) + { + memcpy(frameData, data, l); + data += l; + } + else + memset(frameData, 0, l); + frameData += l; + len -= l; + frameCount -= l; + + ED("PutData: %s=%d len=%d frameCount=%d\n", + data ? "put" : "zero", l, len, frameCount); + if (!frameCount) + { + FinishFrame(); + if (totalSize > 0) + NewFrame(0, 0); + } + } + } +} + +void cAudioEncapsulator::SendIECpause(int type, uchar PTSflags, + const uchar *PTSdata) +{ + StartFrame(AC3_SIZE, PTSflags, PTSdata); + uchar burst[IEC_HDR_SIZE]; + // prepare IEC 60958 data frame + burst[0] = 0xF8; + burst[1] = 0x72; + burst[2] = 0x4E; + burst[3] = 0x1F; + + switch (type) { + default: + case 0: + burst[4] = 7 << 5; // null frame, stream = 7 + burst[5] = 0x00; + burst[6] = 0x00; // No data therein + burst[7] = 0x00; + break; + case 1: + burst[4] = 0x00; // Audio ES Channel empty, wait + burst[5] = 0x03; // for DD Decoder or pause + burst[6] = 0x00; // Trailing frame size is 32 bits payload + burst[7] = 0x20; + break; + case -1: + burst[4] = 0x01; // User stop, skip or error + burst[5] = 0x03; + burst[6] = 0x08; // Trailing frame size is zero + burst[7] = 0x00; + break; + } + PutData(burst, sizeof(burst)); + PutData(0, AC3_SIZE - sizeof(burst)); + FinishFrame(); + muteData = true; +} + +void cAudioEncapsulator::FinishIECFrame(void) +{ + if (!muteData) + { + ED("FinishIECFrame: fillup=%d\n", fillup); + if (fillup) + PutData(0, fillup); + FinishFrame(); + } + muteData = false; + fillup = 0; +} + +void cAudioEncapsulator::SyncFound(const uchar *data) +{ + if (skipped) + { + DEBUG("Decode: skipped %d bytes\n", skipped); + ED("skipped: "); for(int k = -skipped; k < 0; k++) ED("%02x ",data[k]); + ED("\ndata: "); for(int k = 0; k<24; k++) ED("%02x ", data[k]); + ED("\n"); + skipped = 0; + } + uchar pf = 0; + ED("Decode: sync found ptsFlags=%d ptsDelay=%d\n", ptsFlags, ptsDelay); + if (ptsFlags && ptsDelay <= 1) + { + pf = ptsFlags; + ptsFlags = 0; + } + if (firstBurst || mute) + { + SendIECpause(1, pf, ptsData); + if (firstBurst && ++firstBurst>10) + firstBurst = 0; + } + else + StartIECFrame(data, length, pf, ptsData); + PutData(data, SYNC_SIZE); + have = SYNC_SIZE; +} + +void cAudioEncapsulator::Decode(const uchar *data, int len, uchar PTSflags, + int PTSdelay, const uchar *PTSdata) +{ + ED("Decode: enter length=%d have=%d len=%d PTSflags=%d PTSdelay=%d\n", + length, have, len, PTSflags, PTSdelay); + if (PTSflags) + { + // if we are close to the end of an audio frame, but are already + // receiving the start of the next frame, assume a corrupted stream + // and finish the incomplete frame. + if (length && length-have < 20 && !PTSdelay && SyncInfo(data)) + { + int miss = length - have; + DEBUG("Decode: incomplete frame (stream corrupt?). syncing to next. miss=%d\n", miss); + PutData(0, miss); + FinishIECFrame(); + length = have = 0; + } + /* + // we only send PTS info if we're nearly at frame start, except + // if we're signaled to delay the PTS + if (length && have > 40) + { + if(PTSdelay) + ED("Decode: PTS delayed\n"); + else + { + DEBUG("Decode: PTS info dropped length=%d have=%d\n", + length, have); + PTSflags = 0; + } + } + */ + ptsFlags = PTSflags; + ptsData = PTSdata; + ptsDelay = PTSdelay; + //ED("Decode: saved PTS flags=%d delay=%d\n", ptsFlags, ptsDelay); + } + +#if 0 + { + printf("Decode: len=%d\n", len); + for (int i = 0; i < len; ) + { + printf("%04x:", i); + for (int j = 0; j < 16 && i < len; j++) + printf(" %02x", data[i++]); + printf("\n"); + } + } +#endif + + int used = 0; + while (used < len) + { + if (!length) // we are still searching for a header sync + { + if (!have) // buffer is empty, work without buffering + { + if (used + SYNC_SIZE < len) + { + length = SyncInfo(&data[used]); + if (length) + { + ED("Decode: sync found at offset %d (len=%d)\n", + used, length); + SyncFound(&data[used]); + used += SYNC_SIZE; + ptsDelay -= SYNC_SIZE; + continue; + } + else + { + used++; + skipped++; + } + } + else // not enough data to try a sync, buffer the rest + { + ED("Decode: buffering started\n"); + have = len - used; + memcpy(syncBuff, &data[used], have); + used += have; + ptsDelay -= have; + } + } + else // unfortunately buffer is not empty, so continue with + // buffering until sync found + { + int need = std::min(SYNC_SIZE - have, len - used); + if (need) + { + memcpy(&syncBuff[have], &data[used], need); + have += need; + used += need; + ptsDelay -= need; + } + if (have == SYNC_SIZE) + { + length = SyncInfo(syncBuff); + if (length) + { + ED("Decode: (buffered) sync found at offset %d (len=%d)\n",used-7,length); + SyncFound(syncBuff); + continue; + } + else + { + memmove(syncBuff, syncBuff + 1, SYNC_SIZE - 1); + have--; + skipped++; + } + } + } + } + else // we have a header sync and are copying data + { + int need = std::min(length - have, len - used); + if (need) + { + ED("Decode: writing %d\n", need); + PutData(&data[used], need); + have += need; + used += need; + ptsDelay -= need; + if (have == length) + { + FinishIECFrame(); + length = have = 0; + continue; + } + } + } + } + ED("Decode: leave length=%d have=%d len=%d used=%d\n", + length, have, len, used); +} + +// --- cAudioEncapsulatorAC3 -------------------------------------------------- + +class cAudioEncapsulatorAC3 : public cAudioEncapsulator { +private: + virtual int SyncInfo(const uchar *buf); + virtual void StartIECFrame(const uchar *buf, int length, uchar PTSflags, + const uchar *PTSdata); +public: + cAudioEncapsulatorAC3(cRingBufferFrame *rb, int StreamType); +}; + +cAudioEncapsulatorAC3::cAudioEncapsulatorAC3(cRingBufferFrame *rb, + int StreamType) + : cAudioEncapsulator(rb, StreamType) +{} + +int cAudioEncapsulatorAC3::SyncInfo(const uchar *buf) +{ + static const int rate[] = { 32, 40, 48, 56, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 320, 384, 448, + 512, 576, 640 }; + + if ((buf[0] != 0x0B) || (buf[1] != 0x77)) /* syncword */ + return 0; + if (buf[5] >= 0x60) /* bsid >= 12 */ + return 0; + + int frmsizecod = buf[4] & 63; + if (frmsizecod >= 38) + return 0; + int bitrate = rate[frmsizecod >> 1]; + + switch (buf[4] & 0xC0) { + case 0: + return 4 * bitrate; + case 0x40: + return 2 * (320 * bitrate / 147 + (frmsizecod & 1)); + case 0x80: + return 6 * bitrate; + default: + return 0; + } +} + +void cAudioEncapsulatorAC3::StartIECFrame(const uchar *buf, int length, + uchar PTSflags, const uchar *PTSdata) +{ + StartFrame(AC3_SIZE, PTSflags, PTSdata); + fillup = AC3_SIZE - IEC_HDR_SIZE - length; + + // prepare IEC 60958 data frame + uchar burst[IEC_HDR_SIZE]; + burst[0] = 0xF8; + burst[1] = 0x72; + burst[2] = 0x4E; + burst[3] = 0x1F; + burst[4] = (buf[5] & 0x07); // Pc1 + burst[5] = 0x01; // Pc2 AC-3 + burst[6] = ((length * 8) >> 8 ) & 0xFF; // Pd1 + burst[7] = (length * 8) & 0xFF; // Pd2 + PutData(burst,sizeof(burst)); +} + +// --- cAudioEncapsulatorDTS -------------------------------------------------- + +class cAudioEncapsulatorDTS : public cAudioEncapsulator +{ +private: + virtual int SyncInfo(const uchar *buf); + virtual void StartIECFrame(const uchar *buf, int length, uchar PTSflags, + const uchar *PTSdata); +public: + cAudioEncapsulatorDTS(cRingBufferFrame *rb, int StreamType); +}; + +cAudioEncapsulatorDTS::cAudioEncapsulatorDTS(cRingBufferFrame *rb, + int StreamType) + : cAudioEncapsulator(rb, StreamType) +{} + +int cAudioEncapsulatorDTS::SyncInfo(const uchar *buf) +{ + if ((buf[0] != 0x7F) || + (buf[1] != 0xfE) || + (buf[2] != 0x80) || + (buf[3] != 0x01)) + return 0; + + int length = ((buf[5] & 0x03) << 12) | + ((buf[6] & 0xFF) << 4) | + ((buf[7] & 0xF0) >> 4); + + return length + 1; +} + +void cAudioEncapsulatorDTS::StartIECFrame(const uchar *buf, int length, + uchar PTSflags, const uchar *PTSdata) +{ + uchar ac5_type = ((buf[4] & 0x01) << 6) | ((buf[5] >>2) & 0x3F); + uchar ac5_spdif_type; + switch(ac5_type) + { + case 0x0F: + ac5_spdif_type = 0x0B; // DTS + break; + case 0x1F: + ac5_spdif_type = 0x0C; // DTS + break; + case 0x3F: + ac5_spdif_type = 0x0D; // DTS + break; + default: + ac5_spdif_type = 0x00; // DTS + esyslog("DTS: SPDIF type not detected: ac5 type = %X!\n", ac5_type); + break; + } + + if (length > DTS_SIZE-IEC_HDR_SIZE) + { + DEBUG("DTS: length too long %d\n", length); + return; + } + + StartFrame(DTS_SIZE, PTSflags, PTSdata); + fillup = DTS_SIZE - IEC_HDR_SIZE - length; + + // prepare IEC 60958 data frame + uchar burst[IEC_HDR_SIZE]; + burst[0] = 0xF8; + burst[1] = 0x72; + burst[2] = 0x4E; + burst[3] = 0x1F; + burst[4] = 0x00; + burst[5] = ac5_spdif_type; // DTS data + burst[6] = ((length * 8) >> 8) & 0xFF; // ac5_length * 8 + burst[7] = (length * 8) & 0xFF; + PutData(burst,sizeof(burst)); +} + +// --- cMultichannelAudio ----------------------------------------------------- + +cMultichannelAudio::cMultichannelAudio(cRingBufferFrame *rb) +{ + encapsulator = 0; + ringBuffer = rb; + fixed = false; + if (!ringBuffer) + DEBUG("multichannel: no ringbuffer!"); +} + +cMultichannelAudio::~cMultichannelAudio() +{ + delete encapsulator; +} + +void cMultichannelAudio::Clear() +{ + Lock(); + if (encapsulator) + encapsulator->Clear(); + Unlock(); +} + +void cMultichannelAudio::Reset() +{ + Lock(); + delete encapsulator; + encapsulator = 0; + fixed = false; + Unlock(); +} + +/* +void cMultichannelAudio::Mute(bool Mute) +{ + Lock(); + if (encapsulator) + encapsulator->Mute(Mute); + Unlock(); +} +*/ + +int cMultichannelAudio::Check(uchar *b, int length, uchar *header) +{ + Lock(); + int res = 0; + ptsDelay = 0; + offset = 0; + ptsData = 0; + + // get PTS information + ptsFlags = header[7] >> 6; + if (ptsFlags) + ptsData = &header[9]; + + // AC3 frames may span over multiple PES packets. Unfortunately the + // continuation packets start with the aLPCM code sometimes. Some magic + // here to detect this case. + uchar subStreamType = b[0]; + if (subStreamType != aVDR) + subStreamType&=0xF8; + bool aligned = header[6] & 4; + if (!aligned) + { + uchar ost = encapsulator ? encapsulator->StreamType() : 0; + if (!ptsFlags) + { + if ((subStreamType != aLPCM && subStreamType != aSPU) || fixed) + { + if (ost > 0) + { + ED("multichannel: crossing -> keep encapsulator\n"); + subStreamType = ost; + } + else + { + ED("multichannel: crossing -> skip\n"); + res = 1; + goto out; // skip + } + } + } + else if (fixed && ost > 0) + { + ED("multichannel: fixed unaligned -> keep encapsulator\n"); + subStreamType = ost; + } + } + fixed = false; + + switch (subStreamType) + { + case aDTS: + case aAC3: + offset = 4; // skip the DVD stream infos + break; + default: + if (aligned || !ptsFlags) + { + if (encapsulator) + { + Reset(); + DEBUG("multichannel: interrupted encapsulator stream (unknown)\n"); + } + DEBUG("multichannel: unknown substream type %x (skipped)\n", + subStreamType); + res = 1; + goto out; // skip + } + subStreamType = aVDR; + ED("multichannel: assuming aVDR for unknown substream type\n"); + // fall through + case aVDR: + fixed = true; + break; + case aLPCM: + if (encapsulator) + { + Reset(); + DEBUG("multichannel: interrupted encapsulator stream (LPCM)\n"); + } + ED("multichannel: LPCM\n"); + res = 2; + goto out; // pass + case aSPU: + ED("multichannel: SPU stream (skipped)\n"); + res = 1; + goto out; // skip + } + + // If the SubStreamType has changed then select the right encapsulator + if (!encapsulator || encapsulator->StreamType() != subStreamType) + { + DEBUG("multichannel: new encapsulator %x\n", subStreamType); + Reset(); + switch (subStreamType) + { + case aAC3: + case aVDR: // AC3 + encapsulator = new cAudioEncapsulatorAC3(ringBuffer,subStreamType); + break; + case aDTS: // Dts + encapsulator = new cAudioEncapsulatorDTS(ringBuffer,subStreamType); + break; + } + if (!encapsulator) + { + DEBUG("multichannel: no encapsulator\n"); + res = 1; + goto out; // skip + } + } + +out: + ED("HEADER type=%x sub=%x ptsflags=%d length=%d\n", + header[3], subStreamType, ptsFlags, length); + ED("head: "); for(int k = 0; k < 24; k++) ED("%02x ", header[k]); + ED("\ndata: "); for(int k = 0; k < 24; k++) ED("%02x ", b[k]); + ED("\n"); + + Unlock(); + return res; +} + +void cMultichannelAudio::Encapsulate(uchar *b, int length) +{ + Lock(); + if(offset && ptsFlags) + { // get start of the packet to which the PTS belong (DVD only) + if (offset >= 2 && length > offset - 2) + ptsDelay |= b[offset - 2] * 256; + if (offset >= 1 && length > offset - 1) + ptsDelay |= b[offset - 1]; + } + if (length >= offset) + { + if (encapsulator) + encapsulator->Decode(b+offset, length - offset, ptsFlags, + ptsDelay, ptsData); + ptsFlags = 0; + ptsDelay = 0; + offset = 0; + ptsData = 0; + } + else + offset -= length; + Unlock(); +} + +// Local variables: +// mode: c++ +// c-file-style: "stroustrup" +// c-file-offsets: ((inline-open . 0)) +// indent-tabs-mode: t +// End: |