/* * dxr3multichannelaudio.c: * * taken from the AC3overDVB Patch maintained by Stefan Huelswitt * * */ #include #include "dxr3multichannelaudio.h" #include "dxr3log.h" #include //#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 we need 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= 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. Unfortunaly 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: