-/*\r
- Copyright 2006-2008 Mark Calderbank\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "demuxerts.h"\r
-#include "log.h"\r
-#include "video.h"\r
-#include "vdr.h"\r
-#include "audio.h"\r
-\r
-#define PTS_JUMP_MARGIN 10000\r
-#define PTS_ALLOWANCE 90000\r
-\r
-// TODO: PTS class to handle wrapping arithmetic & comparisons?\r
-static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2)\r
-{\r
- // Assume pts1, pts2 < 2^33; calculate shortest distance between\r
- ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1;\r
- if (ret > (1LL<<32)) ret = (1LL<<33) - ret;\r
- return ret;\r
-}\r
-\r
-static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)\r
-{\r
- // Assume pts1, pts2 < 2^33; calculate pts1 - pts2\r
- if (pts1 > pts2)\r
- return pts1 - pts2;\r
- else\r
- return (1LL<<33) + pts1 - pts2;\r
-}\r
-\r
-DemuxerTS::DemuxerTS(int p_vID, int p_aID, int p_subID, int p_tID)\r
-{\r
- vID = p_vID; vActive = false;\r
- aID = p_aID; aActive = false;\r
- subID = p_subID; subActive = false;\r
- atype = 0;\r
- subLength = 0;\r
- tID = p_tID;\r
- havechannelinfo=false;\r
- //doubledframerate=false;\r
- framereserve=0;\r
-}\r
-\r
-void DemuxerTS::flush()\r
-{\r
- partPacket = 0;\r
- parsed = false;\r
- havechannelinfo=false;\r
- Demuxer::flush();\r
- vPacket.init(PESTYPE_VID0);\r
- switch (atype)\r
- {\r
- case 1:\r
- aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);\r
- break;\r
- default:\r
- case 0:\r
- aPacket.init(PESTYPE_AUD0);\r
- break;\r
- }\r
- subPacket.init(PESTYPE_PRIVATE_1);\r
-tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);\r
-\r
- vActive = false;\r
- aActive = false;\r
- subActive = false;\r
- subLength = 0;\r
- tActive = false;\r
- // doubledframerate=false;\r
- framereserve=0;\r
-}\r
-\r
-int DemuxerTS::scan(UCHAR *buf, int len)\r
-{\r
- switch (atype)\r
- {\r
- case 1:\r
- return PESTYPE_PRIVATE_1;\r
- default:\r
- case 0:\r
- return PESTYPE_AUD0;\r
- }\r
-}\r
-\r
-void DemuxerTS::setVID(int p_vID)\r
-{\r
- vID = p_vID;\r
- vPacket.init(PESTYPE_VID0);\r
- vActive = false;\r
-}\r
-\r
-void DemuxerTS::setAID(int p_aID, int type, int streamtype)\r
-{\r
- aID = p_aID;\r
- atype = type;\r
- astreamtype = streamtype;\r
- switch (atype)\r
- {\r
- case 1:\r
- aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);\r
- setAudioStream(PESTYPE_SUBSTREAM_AC30);\r
- break;\r
- default:\r
- case 0:\r
- aPacket.init(PESTYPE_AUD0);\r
- setAudioStream(PESTYPE_AUD0);\r
- break;\r
- }\r
- aActive = false;\r
-}\r
-\r
-void DemuxerTS::setSubID(int p_subID)\r
-{\r
- subID = p_subID;\r
- subPacket.init(PESTYPE_PRIVATE_1);\r
- subActive = false;\r
- subLength = 0;\r
-}\r
-\r
-void DemuxerTS::setTID(int p_tID)\r
-{\r
- tID = p_tID;\r
- tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);\r
- tActive = false;\r
-\r
-}\r
-\r
-\r
-\r
-\r
-int DemuxerTS::findPTS(UCHAR* buf, int len, ULLONG* dest)\r
-{\r
- int scanaid=0;\r
-\r
- while (len >= TS_SIZE)\r
- {\r
- if (*buf != TS_SIG) {buf++;len--; continue;} \r
-\r
- //Pattern scanning won't work for ts\r
-\r
- \r
- int datalen = TS_SIZE - 4;\r
- int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];\r
- UCHAR payload = buf[1] & 0x40;\r
- \r
- if (buf[3] & 0x20) // Adaptation field is present\r
- datalen -= (buf[4] + 1);\r
-\r
- UCHAR* curbuf =buf+ (TS_SIZE - datalen);\r
- \r
-\r
- if (payload) {\r
- if (pid == 0x00) {//PAT, only take first program number, ignore the rest\r
- int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12);\r
- if ((pmtpid >> 13) != 0x07) \r
- {\r
- Log::getInstance()->log("findPTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13));\r
- } \r
- else \r
- {\r
- pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits\r
- PMTPID = pmtpid;\r
- }\r
- \r
- } else if (pid == PMTPID) { //PMT\r
- int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3);\r
- //sectionlength += 4; //include header but subtract crc in the end...\r
- int p = 13; //skip fixed part of pmt\r
- while ( p < sectionlength) {\r
- int streamtype = *(curbuf+p);\r
- p++;\r
- int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1);\r
- p += 2; //skip ES Pid\r
- int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1);\r
- p += 2; //skip ES length\r
- if ((foundpid >> 13) != 0x07)\r
- {\r
- Log::getInstance()->log("findPTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));\r
- }\r
- else \r
- {\r
- foundpid = foundpid & 0x1FFF; //clear upper 3 bits\r
- //int pos=0; UNUSED?\r
- if (streamtype==3 || streamtype ==4) {\r
- scanaid=foundpid;\r
- }\r
- }\r
- p += eslength; //skip es descriptor\r
- }\r
- } else if (pid == scanaid) { \r
- // UINT framelength = ((UINT)curbuf[4] << 8) | curbuf[5]; UNUSED?\r
- \r
- if ( curbuf[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present\r
- {\r
- *dest = ( (ULLONG)(curbuf[9] & 0x0E) << 29 ) |\r
- ( (ULLONG)(curbuf[10]) << 22 ) |\r
- ( (ULLONG)(curbuf[11] & 0xFE) << 14 ) |\r
- ( (ULLONG)(curbuf[12]) << 7 ) |\r
- ( (ULLONG)(curbuf[13] & 0xFE) >> 1 );\r
- return 1;\r
- }\r
- }\r
- }\r
- len-=TS_SIZE;\r
- buf+=TS_SIZE;\r
- }\r
- // No PTS found.\r
- return 0;\r
-}\r
-\r
-void DemuxerTS::setFrameNum(ULONG frame)\r
-{\r
- frameCounting = true;\r
- frameNumber = frame;\r
- framereserve=0;\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setFrameNum %d", frame);\r
-}\r
-\r
-void DemuxerTS::setPacketNum(ULONG npacket)\r
-{\r
- packetCounting = true;\r
- packetNumber = npacket;\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setPacketNum %d", npacket);\r
-}\r
-\r
-int DemuxerTS::put(UCHAR* buf, int len)\r
-{\r
- int ret = 0; // return number of bytes consumed\r
-\r
- bool misaligned_mess=false;\r
- while (partPacket)\r
- {\r
- if (len >= TS_SIZE + 1 - partPacket)\r
- { // Remainder of partial packet is available, plus one\r
- memcpy(store+partPacket, buf, TS_SIZE - partPacket);\r
- ret += TS_SIZE - partPacket;\r
- buf += TS_SIZE - partPacket;\r
- len -= TS_SIZE - partPacket;\r
- partPacket = TS_SIZE;\r
- if (*buf == TS_SIG)\r
- { // Packet is properly terminated\r
- int rc = processTS(store);\r
- if (rc)\r
- partPacket = 0; // Successfully processed\r
- else\r
- return ret; // Try again later.\r
- }\r
- else\r
- { // Packet not terminated. Find another candidate, and shift store\r
- if (!misaligned_mess) {\r
- Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!A");\r
- misaligned_mess=true; // do not alarm more than once\r
- }\r
- int search = 1;\r
- while (search < partPacket && store[search] != TS_SIG)\r
- search++;\r
- partPacket -= search;\r
- if (partPacket) memcpy(store, store+search, partPacket);\r
- }\r
- }\r
- else\r
- { // Still don't have complete packet. Consume what we do have.\r
- memcpy(store+partPacket, buf, len);\r
- partPacket += len;\r
- ret += len;\r
- return ret;\r
- }\r
- }\r
-\r
- // Position ourselves at a candidate TS packet\r
- while (len > 0 && *buf != TS_SIG)\r
- {\r
- if (!misaligned_mess) {\r
- Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!B");\r
- misaligned_mess=true; // do not alarm more than once\r
- }\r
- buf++; ret++; len--;\r
- }\r
-\r
- while (len)\r
- {\r
- if (len < TS_SIZE + 1)\r
- { // Not enough data. Store what we have.\r
- memcpy(store, buf, len);\r
- partPacket = len;\r
- ret += len;\r
- return ret;\r
- }\r
-\r
- if (buf[TS_SIZE] != TS_SIG)\r
- { // Not terminated correctly.\r
- buf++; ret++; len--;\r
- while (len > 0 && *buf != TS_SIG)\r
- {\r
- buf++; ret++; len--;\r
- }\r
- }\r
- else\r
- {\r
- int rc = processTS(buf);\r
- if (rc)\r
- { // Successfully processed\r
- buf += TS_SIZE; ret += TS_SIZE; len -= TS_SIZE;\r
- }\r
- else\r
- { // Processing failed.\r
- return ret;\r
- }\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-\r
-int DemuxerTS::processTS(UCHAR* buf)\r
-{\r
- int datalen = TS_SIZE - 4;\r
-\r
- int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];\r
- UCHAR payload = buf[1] & 0x40;\r
-\r
-\r
- if (buf[3] & 0x20) // Adaptation field is present\r
- datalen -= (buf[4] + 1);\r
- if (datalen < 0) // Error in stream TODO log this\r
- return 1;\r
- if (datalen == 0) // Null packet\r
- return 1;\r
- // if (!(buf[3] &0x10)) return 1; // no payload\r
- buf += (TS_SIZE - datalen);\r
-\r
- if (payload)\r
- {\r
- int rc = 1;\r
- if (pid == 0x00) {//PAT, only take first program number, ignore the rest\r
- int pmtpid = (*(buf+11)<< 8) | *(buf+12);\r
- if ((pmtpid >> 13) != 0x07) \r
- {\r
- Log::getInstance()->log("ProcessTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(buf+11),*(buf+12), (pmtpid >> 13));\r
- } \r
- else \r
- {\r
- pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits\r
- PMTPID = pmtpid;\r
- }\r
- return 1;\r
- }\r
- if (pid == PMTPID) \r
- { //PMT\r
- int sectionlength = ((*(buf+2) << 8) & 0x0F ) | *(buf+3);\r
- //sectionlength += 4; //include header but subtract crc in the end...\r
- int p = 13; //skip fixed part of pmt\r
- Channel new_channelinfo;\r
- new_channelinfo.numAPids=0;\r
- new_channelinfo.numDPids=0;\r
- new_channelinfo.numSPids=0;\r
- new_channelinfo.number=0;\r
- new_channelinfo.type=VDR::RADIO;\r
- new_channelinfo.name=NULL;\r
- new_channelinfo.tpid=0xFFFFF; //unused, check this\r
- new_channelinfo.vpid=0xFFFFF; //unused, check this\r
- new_channelinfo.index=0;\r
-\r
- new_channelinfo.apids.clear();\r
- new_channelinfo.dpids.clear();\r
- new_channelinfo.spids.clear();\r
- \r
- while ( p < sectionlength) {\r
- int streamtype = *(buf+p);\r
- p++;\r
- int foundpid = (*(buf+p)<< 8) | *(buf+p+1);\r
- p += 2; //skip ES Pid\r
- int eslength = ((*(buf+p) << 8) & 0x0F ) | *(buf+p+1);\r
- p += 2; //skip ES length\r
- if ((foundpid >> 13) != 0x07)\r
- {\r
- Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));\r
- }\r
- else \r
- {\r
- foundpid = foundpid & 0x1FFF; //clear upper 3 bits\r
- bool notfound=false;\r
- bool nolang=true;\r
- int pos=0;\r
- // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID is %x %x", foundpid,streamtype);\r
- switch (streamtype)\r
- {\r
- case 0x1B: //MPEG 4 for future use\r
- case 1:\r
- case 2: { //video\r
- if (foundpid != getVID()) \r
- setVID(foundpid);\r
- new_channelinfo.type=VDR::VIDEO;\r
- new_channelinfo.vstreamtype=streamtype;\r
- new_channelinfo.vpid=foundpid;\r
- if (streamtype==0x1b) h264=true;\r
- else h264=false;\r
-\r
- // Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set video PID to %x", foundpid);\r
- }break;\r
- case 0x0F: //AAC ADTS packaging\r
- case 0x11: // LATM packaging\r
- case 3:\r
- case 4: { //audio\r
- apid newapid;\r
- newapid.pid = foundpid;\r
- newapid.desc[0]=0;\r
- newapid.type=streamtype;\r
- pos=0;\r
- nolang=true;\r
- while (pos< eslength && nolang) {\r
- switch (buf[p+pos]) {\r
- case 0x0A: {\r
- newapid.desc[0]=buf[p+pos+2];\r
- newapid.desc[1]=buf[p+pos+3];\r
- newapid.desc[2]=buf[p+pos+4];\r
- newapid.desc[3]=0;\r
- nolang=false;\r
- // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDLANG is %s", newapid.desc);\r
- } break;\r
- };\r
-\r
- pos+=2+buf[p+pos+1];\r
- }\r
-\r
- new_channelinfo.apids.push_back(newapid);\r
- new_channelinfo.numAPids++;\r
- if (getAID() == 0 && Audio::getInstance()->streamTypeSupported(streamtype)) { //set unset AID to first audio pid that reports itself\r
- setAID(foundpid,0,streamtype);\r
- Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set audio PID to %x", foundpid);\r
- }\r
- } break;\r
- case 5:\r
- case 6: { //Private Data \r
- apid newapid;\r
- newapid.pid = foundpid;\r
- newapid.desc[0]=0; //set it in player\r
- pos=0;\r
- notfound=true;\r
- nolang=true;\r
- int type=0;\r
- \r
- while (pos< eslength && (notfound || nolang)) {\r
- switch (buf[p+pos]) {\r
- case 0x7A: // Enhanced Ac3 Desriptor\r
- case 0x6A: {//Ac3 descriptor \r
- newapid.type=buf[p+pos];\r
- type=1;\r
-\r
- notfound=false;\r
- } break; \r
- case 0x59: {//SubtitlingDescriptor\r
- type=2;\r
- newapid.type=buf[p+pos];\r
- newapid.desc[0]=buf[p+pos+2];\r
- newapid.desc[1]=buf[p+pos+3];\r
- newapid.desc[2]=buf[p+pos+4];\r
- newapid.desc[3]=0;\r
- newapid.data1=(buf[p+pos+5]<<8) |(buf[p+pos+6]);\r
- newapid.data2=(buf[p+pos+7]<<8) |(buf[p+pos+8]);\r
- // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDSUB is %s", newapid.desc);\r
- notfound=false;\r
- nolang=false;\r
- } break;\r
- case 0x0A: {\r
- newapid.desc[0]=buf[p+pos+2];\r
- newapid.desc[1]=buf[p+pos+3];\r
- newapid.desc[2]=buf[p+pos+4];\r
- newapid.desc[3]=0;\r
- nolang=false;\r
- // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDLANG is %s", newapid.desc);\r
- } break;\r
- case 0x56: {\r
- type=3;\r
- new_channelinfo.tpid=foundpid;\r
- notfound=false;\r
- } break;\r
- };\r
- pos+=2+buf[p+pos+1];\r
- }\r
- if (type==1) {\r
- new_channelinfo.dpids.push_back(newapid);\r
- new_channelinfo.numDPids++;\r
- } else if (type==2) {\r
- new_channelinfo.spids.push_back(newapid);\r
- new_channelinfo.numSPids++;\r
- }\r
-\r
- } break;\r
- default://TODO how about subtitles and second audio pids\r
- break;\r
- }\r
- }\r
- \r
- p += eslength; //skip es descriptor\r
- \r
- \r
- }\r
- bool audioPIDpresent=false; //Check if pids chnages\r
- ULONG i;\r
- for (i=0;i<channelinfo.numAPids;i++) {\r
- if (aID == (int)channelinfo.apids[i].pid) {\r
- audioPIDpresent=true;\r
- }\r
- }\r
- for (i=0;i<channelinfo.numDPids && (! audioPIDpresent);i++) {\r
- if (aID == (int)channelinfo.dpids[i].pid) {\r
- audioPIDpresent=true;\r
- }\r
- }\r
- if (! audioPIDpresent) {\r
- bool found=false;\r
- if (channelinfo.numAPids>0) {\r
- int j=0;\r
- while (j<channelinfo.numAPids && !found) {\r
- if (Audio::getInstance()->streamTypeSupported(channelinfo.apids[j].type)) {\r
- found =true;\r
- setAID(channelinfo.apids[j].pid,0,channelinfo.apids[j].type);\r
- }\r
- j++;\r
- }\r
-\r
- }\r
- if (channelinfo.numDPids>0 && !found) {\r
- int j=0;\r
- while (j<channelinfo.numDPids && !found) {\r
- if (Audio::getInstance()->streamTypeSupported(channelinfo.dpids[j].type)) {\r
- found =true;\r
- setAID(channelinfo.dpids[j].pid,1,channelinfo.dpids[j].type);\r
- }\r
- i++;\r
- }\r
- }\r
- }\r
-\r
- channelinfo=new_channelinfo;\r
-\r
-\r
- havechannelinfo=true;\r
-\r
- return 1;\r
- }\r
- \r
-\r
- \r
-\r
- if (pid == vID)\r
- {\r
- if (vActive)\r
- {\r
- if (!parsed)\r
- {\r
- parseTSPacketDetails(vPacket);\r
- parsed = true;\r
- }\r
- rc = submitPacket(vPacket);\r
- }\r
- vActive = true;\r
- }\r
- if (pid == aID)\r
- {\r
- if (aActive)\r
- {\r
- if (!parsed)\r
- {\r
- parseTSPacketDetails(aPacket);\r
- parsed = true;\r
- }\r
- rc = submitPacket(aPacket);\r
- }\r
- aActive = true;\r
- }\r
- if (pid == subID)\r
- {\r
- if (subActive)\r
- {\r
- if (!parsed)\r
- {\r
- parseTSPacketDetails(subPacket);\r
- parsed = true;\r
- }\r
- rc = submitPacket(subPacket);\r
- }\r
- subActive = true;\r
- }\r
- if (isteletextdecoded && pid == tID)\r
- {\r
- if (tActive)\r
- {\r
- if (!parsed)\r
- {\r
- parseTSPacketDetails(tPacket);\r
- parsed = true;\r
- }\r
- rc = submitPacket(tPacket);\r
- }\r
- tActive = true;\r
- }\r
- if (rc == 0) return 0;\r
-\r
- parsed = false;\r
- if (pid == vID)\r
- {\r
- vPacket.init(PESTYPE_VID0);\r
- buf += 6; datalen -= 6;\r
- }\r
- if (pid == aID)\r
- {\r
- switch (atype)\r
- {\r
- case 1:\r
- aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);\r
- break;\r
- default:\r
- case 0:\r
- aPacket.init(PESTYPE_AUD0);\r
- break;\r
- }\r
- buf += 6; datalen -= 6;\r
- }\r
- if (pid == subID)\r
- {\r
- subPacket.init(PESTYPE_PRIVATE_1);\r
- subLength = (buf[4] << 8) + buf[5];\r
- buf += 6; datalen -= 6;\r
- }\r
- if (isteletextdecoded && pid == tID)\r
- {\r
- tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);\r
- buf += 6; datalen -= 6;\r
- }\r
- }\r
-\r
- if ( (pid == vID && vActive) ||\r
- (pid == aID && aActive) ||\r
- (pid == tID && tActive) ||\r
- (pid == subID && subActive) )\r
- {\r
- PESPacket* packet = NULL;\r
- if (pid == vID) packet = &vPacket;\r
- if (pid == aID) packet = &aPacket;\r
- if (pid == subID) {\r
- packet = &subPacket;\r
- }\r
- if (pid == tID) packet = &tPacket;\r
- if (packet != NULL)\r
- {\r
- if (packet->write(buf, datalen) == 0)\r
- { // Writing to packet failed. It has overflowed.\r
- if (!parsed)\r
- {\r
- parseTSPacketDetails(*packet);\r
- parsed = true;\r
- }\r
- if (submitPacket(*packet) == 0) return 0;\r
- parsed = false;\r
- packet->truncate();\r
- packet->write((UCHAR*)"\200\000\000", 3);\r
- packet->write(buf, datalen);\r
- }\r
- }\r
- }\r
-\r
- if (pid == subID && subActive && subPacket.getLength() == subLength)\r
- {\r
- parsePacketDetails(subPacket);\r
-Log::getInstance()->log("DEMUXERTS", Log::DEBUG, "SUBMITTING A SUBTITLE PACKET %d %x", subLength, subPacket.getSubstream());\r
- submitPacket(subPacket);\r
- subActive = false;\r
- }\r
-\r
- return 1;\r
-}\r
-\r
-ULONG DemuxerTS::getPacketNum()\r
-{\r
- return packetNumber;\r
-}\r
-\r
-ULONG DemuxerTS::getFrameNumFromPTS(ULLONG pts)\r
-{\r
- ULLONG difference = (1LL<<33);\r
- ULONG ref_frame = 0;\r
- int total = 0, actual = 0;\r
- if (pts==0) return 0; //we are in startup\r
- pts_map_mutex.Lock();\r
- PTSMap::iterator iter = pts_map.begin();\r
- while (iter != pts_map.end())\r
- {\r
- ++total;\r
- //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts1 %lld pts2 %lld", pts, iter->pts);\r
- if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE)\r
- {\r
- difference = 0;\r
- ref_frame = iter->frame;\r
- actual = total;\r
- break;\r
- }\r
- ULLONG newdiff = PTSDifference(pts, iter->pts);\r
- if (newdiff < difference)\r
- {\r
- difference = newdiff;\r
- ref_frame = iter->frame;\r
- actual = total;\r
- }\r
- ++iter;\r
- }\r
- if (total > 1 && actual == 1) // We are using the most recent PTS ref.\r
- { // Delete the rest.\r
- iter = pts_map.begin(); iter++;\r
- pts_map.erase(iter, pts_map.end());\r
- }\r
- pts_map_mutex.Unlock();\r
- \r
- //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts %lld deleted %d difference %lld", pts, total,difference);\r
-\r
- if (difference == (1LL<<33))\r
- return 0; // We cannot make sense of the pts\r
- else\r
- return ref_frame + difference * fps / 90000;\r
-}\r
-\r
-\r
-void DemuxerTS::parseTSPacketDetails(PESPacket &packet) // Only important stuff for paket counting reminas\r
-{\r
- parsePacketDetails(packet);\r
- if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 &&\r
- packet.getPacketType() <= PESTYPE_AUDMAX)\r
- {\r
- packetNumber++;\r
- }\r
- UINT pictsinpacket=packet.countPictureHeaders(h264);\r
- \r
- UINT numpicts=0;\r
- /* if (!doubledframerate)\r
- {*/\r
- numpicts=pictsinpacket;\r
- /* }\r
- else\r
- {\r
- numpicts=(pictsinpacket+framereserve)>>1;\r
- framereserve=(pictsinpacket+framereserve)%2;\r
- }*/\r
-\r
-\r
- if (frameCounting && numpicts &&\r
- packet.getPacketType() >= PESTYPE_VID0 &&\r
- packet.getPacketType() <= PESTYPE_VIDMAX)\r
- {\r
- frameNumber+=numpicts;\r
- ULONG frame_num = frameNumber;\r
- if ((h264 || packet.findSeqHeader(h264) > 1) && packet.hasPTS())\r
- {\r
- PTSMapEntry me;\r
- pts_map_mutex.Lock();\r
- if (pts_map.empty())\r
- {\r
- me.pts = packet.getPTS();\r
- me.frame = frame_num;\r
- pts_map_mutex.Unlock();\r
- pts_map_mutex.Lock();\r
- pts_map.push_front(me);\r
- }\r
- me = pts_map.front();\r
- pts_map_mutex.Unlock();\r
-\r
- //UINT fps = Video::getInstance()->getFPS();\r
- double tfps=fps;\r
- // if (doubledframerate) tfps*=2.;\r
- ULLONG pts_expected = me.pts + 90000*((int)(((double)(frame_num - me.frame)) / tfps));\r
-\r
- while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33);\r
-\r
- // this was a workaround for a vdr bug, now fixed, for older recordings deleter the index file\r
-\r
- /*\r
- if (!doubledframerate \r
- &&(abs((long long)PTSDistance(pts_expected, packet.getPTS())-1800) <= 1 \r
- || abs((long long)PTSDistance(pts_expected, packet.getPTS())-1501) <= 1)) {\r
- doubledframerate=true; //Detected p50 or p60\r
- }*/\r
-\r
- if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump!\r
- {\r
- me.pts = packet.getPTS();\r
- me.frame = frame_num;\r
- pts_map_mutex.Lock();\r
- pts_map.push_front(me);\r
- pts_map_mutex.Unlock();\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-bool DemuxerTS::scanForVideo(UCHAR* buf, UINT len, bool &ish264)\r
-{\r
- int pmtpidy=0;\r
-\r
- while (len >= TS_SIZE)\r
- {\r
- if (*buf != TS_SIG) {buf++;len--; continue;} \r
-\r
- //Pattern scanning won't work for ts\r
-\r
- \r
- int datalen = TS_SIZE - 4;\r
- int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];\r
- UCHAR payload = buf[1] & 0x40;\r
- \r
- if (buf[3] & 0x20) // Adaptation field is present\r
- datalen -= (buf[4] + 1);\r
-\r
- UCHAR* curbuf =buf+ (TS_SIZE - datalen);\r
- \r
- if (payload) {\r
- if (pid == 0x00) {//PAT, only take first program number, ignore the rest\r
- int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12);\r
- if ((pmtpid >> 13) != 0x07) \r
- {\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13));\r
- } \r
- else \r
- {\r
- pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits\r
- pmtpidy = pmtpid;\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMT pid%02x",pmtpid );\r
- }\r
- \r
- } else if (pid == pmtpidy) { //PMT\r
- int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3);\r
- //sectionlength += 4; //include header but subtract crc in the end...\r
- int p = 13; //skip fixed part of pmt\r
- while ( p < sectionlength) {\r
- int streamtype = *(curbuf+p);\r
- p++;\r
- int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1);\r
- p += 2; //skip ES Pid\r
- int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1);\r
- p += 2; //skip ES length\r
- if ((foundpid >> 13) != 0x07)\r
- {\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));\r
- }\r
- else \r
- {\r
- foundpid = foundpid & 0x1FFF; //clear upper 3 bits\r
- // int pos=0; UNUSED?\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Pid found %02x type %02x",foundpid ,streamtype);\r
- if (streamtype==1 || streamtype ==2) {\r
- ish264=false;\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found Mpeg2 Video");\r
- return true;\r
- }\r
- if (streamtype==0x1b) {\r
- ish264=true;\r
- Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found h264 Video");\r
- return true;\r
- }\r
- }\r
- p += eslength; //skip es descriptor\r
- }\r
- ish264=false;\r
- return false;\r
- } \r
- }\r
- len-=TS_SIZE;\r
- buf+=TS_SIZE;\r
- }\r
- ish264=false;\r
- return false;\r
-}\r
-\r
-UINT DemuxerTS::stripAudio(UCHAR* buf, UINT len) //it has to be adapted\r
-{ \r
- //This function strips all TS Headers and non video payload\r
- UINT readpos=0;\r
- UINT writepos=0;\r
- PESPacket destpaket;\r
- bool started=true;\r
- while (readpos < len ) {\r
- if (buf[readpos] != TS_SIG) {readpos++; continue;} \r
- UINT oldreadpos=readpos;\r
-\r
- int datalen = TS_SIZE - 4;\r
- int pid = ( (buf[readpos+1] & 0x1F) << 8 ) | buf[readpos+2];\r
- UCHAR payload = buf[readpos+1] & 0x40;\r
- if (buf[readpos+3] & 0x20) { // Adaptation field is present\r
- datalen -= (buf[readpos+4] + 1);\r
- }\r
- if (datalen < 0) {// Error in stream TODO log this\r
- return 0;\r
- }\r
- if (datalen == 0) {// Null packet\r
- readpos=oldreadpos+TS_SIZE;\r
- continue;\r
- }\r
- readpos += (TS_SIZE - datalen);\r
- UINT towrite=min(datalen,len-readpos);\r
- if (pid == vID) {\r
- if (payload) {\r
- if (started) {\r
- parsePacketDetails(destpaket);\r
- memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());\r
- writepos+=destpaket.getSize();\r
- }\r
- destpaket.init(PESTYPE_VID0);\r
- readpos += 6; towrite -= 6;\r
- started=true;\r
- }\r
-\r
- if (started) {\r
- if (!destpaket.write(buf+readpos,towrite)) {\r
- parsePacketDetails(destpaket);\r
- memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());\r
- writepos+=destpaket.getSize();\r
- destpaket.truncate();\r
- destpaket.write((UCHAR*)"\200\000\000", 3);\r
- destpaket.write(buf+readpos,towrite);\r
-\r
- }\r
- }\r
- \r
- \r
-\r
- }\r
- readpos=oldreadpos+TS_SIZE;\r
- }\r
- parsePacketDetails(destpaket);\r
- memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());\r
- writepos+=destpaket.getSize();\r
- \r
- return writepos;\r
-} \r
-\r
-\r
-\r
+/*
+ Copyright 2006-2008 Mark Calderbank
+
+ This file is part of VOMP.
+
+ VOMP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ VOMP 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with VOMP; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "demuxerts.h"
+#include "log.h"
+#include "video.h"
+#include "vdr.h"
+#include "audio.h"
+
+#define PTS_JUMP_MARGIN 10000
+#define PTS_ALLOWANCE 90000
+
+// TODO: PTS class to handle wrapping arithmetic & comparisons?
+static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2)
+{
+ // Assume pts1, pts2 < 2^33; calculate shortest distance between
+ ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1;
+ if (ret > (1LL<<32)) ret = (1LL<<33) - ret;
+ return ret;
+}
+
+static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
+{
+ // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
+ if (pts1 > pts2)
+ return pts1 - pts2;
+ else
+ return (1LL<<33) + pts1 - pts2;
+}
+
+DemuxerTS::DemuxerTS(int p_vID, int p_aID, int p_subID, int p_tID)
+{
+ vID = p_vID; vActive = false;
+ aID = p_aID; aActive = false;
+ subID = p_subID; subActive = false;
+ atype = 0;
+ subLength = 0;
+ tID = p_tID;
+ havechannelinfo=false;
+ //doubledframerate=false;
+ framereserve=0;
+}
+
+void DemuxerTS::flush()
+{
+ partPacket = 0;
+ parsed = false;
+ havechannelinfo=false;
+ Demuxer::flush();
+ vPacket.init(PESTYPE_VID0);
+ switch (atype)
+ {
+ case 1:
+ aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);
+ break;
+ default:
+ case 0:
+ aPacket.init(PESTYPE_AUD0);
+ break;
+ }
+ subPacket.init(PESTYPE_PRIVATE_1);
+tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);
+
+ vActive = false;
+ aActive = false;
+ subActive = false;
+ subLength = 0;
+ tActive = false;
+ // doubledframerate=false;
+ framereserve=0;
+}
+
+int DemuxerTS::scan(UCHAR *buf, int len)
+{
+ switch (atype)
+ {
+ case 1:
+ return PESTYPE_PRIVATE_1;
+ default:
+ case 0:
+ return PESTYPE_AUD0;
+ }
+}
+
+void DemuxerTS::setVID(int p_vID)
+{
+ vID = p_vID;
+ vPacket.init(PESTYPE_VID0);
+ vActive = false;
+}
+
+void DemuxerTS::setAID(int p_aID, int type, int streamtype)
+{
+ aID = p_aID;
+ atype = type;
+ astreamtype = streamtype;
+ switch (atype)
+ {
+ case 1:
+ aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);
+ setAudioStream(PESTYPE_SUBSTREAM_AC30);
+ break;
+ default:
+ case 0:
+ aPacket.init(PESTYPE_AUD0);
+ setAudioStream(PESTYPE_AUD0);
+ break;
+ }
+ aActive = false;
+}
+
+void DemuxerTS::setSubID(int p_subID)
+{
+ subID = p_subID;
+ subPacket.init(PESTYPE_PRIVATE_1);
+ subActive = false;
+ subLength = 0;
+}
+
+void DemuxerTS::setTID(int p_tID)
+{
+ tID = p_tID;
+ tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);
+ tActive = false;
+
+}
+
+
+
+
+int DemuxerTS::findPTS(UCHAR* buf, int len, ULLONG* dest)
+{
+ int scanaid=0;
+
+ while (len >= TS_SIZE)
+ {
+ if (*buf != TS_SIG) {buf++;len--; continue;}
+
+ //Pattern scanning won't work for ts
+
+
+ int datalen = TS_SIZE - 4;
+ int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];
+ UCHAR payload = buf[1] & 0x40;
+
+ if (buf[3] & 0x20) // Adaptation field is present
+ datalen -= (buf[4] + 1);
+
+ UCHAR* curbuf =buf+ (TS_SIZE - datalen);
+
+
+ if (payload) {
+ if (pid == 0x00) {//PAT, only take first program number, ignore the rest
+ int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12);
+ if ((pmtpid >> 13) != 0x07)
+ {
+ Log::getInstance()->log("findPTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13));
+ }
+ else
+ {
+ pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits
+ PMTPID = pmtpid;
+ }
+
+ } else if (pid == PMTPID) { //PMT
+ int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3);
+ //sectionlength += 4; //include header but subtract crc in the end...
+ int p = 13; //skip fixed part of pmt
+ while ( p < sectionlength) {
+ int streamtype = *(curbuf+p);
+ p++;
+ int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1);
+ p += 2; //skip ES Pid
+ int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1);
+ p += 2; //skip ES length
+ if ((foundpid >> 13) != 0x07)
+ {
+ Log::getInstance()->log("findPTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));
+ }
+ else
+ {
+ foundpid = foundpid & 0x1FFF; //clear upper 3 bits
+ //int pos=0; UNUSED?
+ if (streamtype==3 || streamtype ==4) {
+ scanaid=foundpid;
+ }
+ }
+ p += eslength; //skip es descriptor
+ }
+ } else if (pid == scanaid) {
+ // UINT framelength = ((UINT)curbuf[4] << 8) | curbuf[5]; UNUSED?
+
+ if ( curbuf[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
+ {
+ *dest = ( (ULLONG)(curbuf[9] & 0x0E) << 29 ) |
+ ( (ULLONG)(curbuf[10]) << 22 ) |
+ ( (ULLONG)(curbuf[11] & 0xFE) << 14 ) |
+ ( (ULLONG)(curbuf[12]) << 7 ) |
+ ( (ULLONG)(curbuf[13] & 0xFE) >> 1 );
+ return 1;
+ }
+ }
+ }
+ len-=TS_SIZE;
+ buf+=TS_SIZE;
+ }
+ // No PTS found.
+ return 0;
+}
+
+void DemuxerTS::setFrameNum(ULONG frame)
+{
+ frameCounting = true;
+ frameNumber = frame;
+ framereserve=0;
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setFrameNum %d", frame);
+}
+
+void DemuxerTS::setPacketNum(ULONG npacket)
+{
+ packetCounting = true;
+ packetNumber = npacket;
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setPacketNum %d", npacket);
+}
+
+int DemuxerTS::put(UCHAR* buf, int len)
+{
+ int ret = 0; // return number of bytes consumed
+
+ bool misaligned_mess=false;
+ while (partPacket)
+ {
+ if (len >= TS_SIZE + 1 - partPacket)
+ { // Remainder of partial packet is available, plus one
+ memcpy(store+partPacket, buf, TS_SIZE - partPacket);
+ ret += TS_SIZE - partPacket;
+ buf += TS_SIZE - partPacket;
+ len -= TS_SIZE - partPacket;
+ partPacket = TS_SIZE;
+ if (*buf == TS_SIG)
+ { // Packet is properly terminated
+ int rc = processTS(store);
+ if (rc)
+ partPacket = 0; // Successfully processed
+ else
+ return ret; // Try again later.
+ }
+ else
+ { // Packet not terminated. Find another candidate, and shift store
+ if (!misaligned_mess) {
+ Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!A");
+ misaligned_mess=true; // do not alarm more than once
+ }
+ int search = 1;
+ while (search < partPacket && store[search] != TS_SIG)
+ search++;
+ partPacket -= search;
+ if (partPacket) memcpy(store, store+search, partPacket);
+ }
+ }
+ else
+ { // Still don't have complete packet. Consume what we do have.
+ memcpy(store+partPacket, buf, len);
+ partPacket += len;
+ ret += len;
+ return ret;
+ }
+ }
+
+ // Position ourselves at a candidate TS packet
+ while (len > 0 && *buf != TS_SIG)
+ {
+ if (!misaligned_mess) {
+ Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!B");
+ misaligned_mess=true; // do not alarm more than once
+ }
+ buf++; ret++; len--;
+ }
+
+ while (len)
+ {
+ if (len < TS_SIZE + 1)
+ { // Not enough data. Store what we have.
+ memcpy(store, buf, len);
+ partPacket = len;
+ ret += len;
+ return ret;
+ }
+
+ if (buf[TS_SIZE] != TS_SIG)
+ { // Not terminated correctly.
+ buf++; ret++; len--;
+ while (len > 0 && *buf != TS_SIG)
+ {
+ buf++; ret++; len--;
+ }
+ }
+ else
+ {
+ int rc = processTS(buf);
+ if (rc)
+ { // Successfully processed
+ buf += TS_SIZE; ret += TS_SIZE; len -= TS_SIZE;
+ }
+ else
+ { // Processing failed.
+ return ret;
+ }
+ }
+ }
+ return ret;
+}
+
+
+int DemuxerTS::processTS(UCHAR* buf)
+{
+ int datalen = TS_SIZE - 4;
+
+ int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];
+ UCHAR payload = buf[1] & 0x40;
+
+
+ if (buf[3] & 0x20) // Adaptation field is present
+ datalen -= (buf[4] + 1);
+ if (datalen < 0) // Error in stream TODO log this
+ return 1;
+ if (datalen == 0) // Null packet
+ return 1;
+ // if (!(buf[3] &0x10)) return 1; // no payload
+ buf += (TS_SIZE - datalen);
+
+ if (payload)
+ {
+ int rc = 1;
+ if (pid == 0x00) {//PAT, only take first program number, ignore the rest
+ int pmtpid = (*(buf+11)<< 8) | *(buf+12);
+ if ((pmtpid >> 13) != 0x07)
+ {
+ Log::getInstance()->log("ProcessTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(buf+11),*(buf+12), (pmtpid >> 13));
+ }
+ else
+ {
+ pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits
+ PMTPID = pmtpid;
+ }
+ return 1;
+ }
+ if (pid == PMTPID)
+ { //PMT
+ int sectionlength = ((*(buf+2) << 8) & 0x0F ) | *(buf+3);
+ //sectionlength += 4; //include header but subtract crc in the end...
+ int p = 13; //skip fixed part of pmt
+ Channel new_channelinfo;
+ new_channelinfo.numAPids=0;
+ new_channelinfo.numDPids=0;
+ new_channelinfo.numSPids=0;
+ new_channelinfo.number=0;
+ new_channelinfo.type=VDR::RADIO;
+ new_channelinfo.name=NULL;
+ new_channelinfo.tpid=0xFFFFF; //unused, check this
+ new_channelinfo.vpid=0xFFFFF; //unused, check this
+ new_channelinfo.index=0;
+
+ new_channelinfo.apids.clear();
+ new_channelinfo.dpids.clear();
+ new_channelinfo.spids.clear();
+
+ while ( p < sectionlength) {
+ int streamtype = *(buf+p);
+ p++;
+ int foundpid = (*(buf+p)<< 8) | *(buf+p+1);
+ p += 2; //skip ES Pid
+ int eslength = ((*(buf+p) << 8) & 0x0F ) | *(buf+p+1);
+ p += 2; //skip ES length
+ if ((foundpid >> 13) != 0x07)
+ {
+ Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));
+ }
+ else
+ {
+ foundpid = foundpid & 0x1FFF; //clear upper 3 bits
+ bool notfound=false;
+ bool nolang=true;
+ int pos=0;
+ // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID is %x %x", foundpid,streamtype);
+ switch (streamtype)
+ {
+ case 0x1B: //MPEG 4 for future use
+ case 1:
+ case 2: { //video
+ if (foundpid != getVID())
+ setVID(foundpid);
+ new_channelinfo.type=VDR::VIDEO;
+ new_channelinfo.vstreamtype=streamtype;
+ new_channelinfo.vpid=foundpid;
+ if (streamtype==0x1b) h264=true;
+ else h264=false;
+
+ // Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set video PID to %x", foundpid);
+ }break;
+ case 0x0F: //AAC ADTS packaging
+ case 0x11: // LATM packaging
+ case 3:
+ case 4: { //audio
+ apid newapid;
+ newapid.pid = foundpid;
+ newapid.desc[0]=0;
+ newapid.type=streamtype;
+ pos=0;
+ nolang=true;
+ while (pos< eslength && nolang) {
+ switch (buf[p+pos]) {
+ case 0x0A: {
+ newapid.desc[0]=buf[p+pos+2];
+ newapid.desc[1]=buf[p+pos+3];
+ newapid.desc[2]=buf[p+pos+4];
+ newapid.desc[3]=0;
+ nolang=false;
+ // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDLANG is %s", newapid.desc);
+ } break;
+ };
+
+ pos+=2+buf[p+pos+1];
+ }
+
+ new_channelinfo.apids.push_back(newapid);
+ new_channelinfo.numAPids++;
+ if (getAID() == 0 && Audio::getInstance()->streamTypeSupported(streamtype)) { //set unset AID to first audio pid that reports itself
+ setAID(foundpid,0,streamtype);
+ Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set audio PID to %x", foundpid);
+ }
+ } break;
+ case 5:
+ case 6: { //Private Data
+ apid newapid;
+ newapid.pid = foundpid;
+ newapid.desc[0]=0; //set it in player
+ pos=0;
+ notfound=true;
+ nolang=true;
+ int type=0;
+
+ while (pos< eslength && (notfound || nolang)) {
+ switch (buf[p+pos]) {
+ case 0x7A: // Enhanced Ac3 Desriptor
+ case 0x6A: {//Ac3 descriptor
+ newapid.type=buf[p+pos];
+ type=1;
+
+ notfound=false;
+ } break;
+ case 0x59: {//SubtitlingDescriptor
+ type=2;
+ newapid.type=buf[p+pos];
+ newapid.desc[0]=buf[p+pos+2];
+ newapid.desc[1]=buf[p+pos+3];
+ newapid.desc[2]=buf[p+pos+4];
+ newapid.desc[3]=0;
+ newapid.data1=(buf[p+pos+5]<<8) |(buf[p+pos+6]);
+ newapid.data2=(buf[p+pos+7]<<8) |(buf[p+pos+8]);
+ // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDSUB is %s", newapid.desc);
+ notfound=false;
+ nolang=false;
+ } break;
+ case 0x0A: {
+ newapid.desc[0]=buf[p+pos+2];
+ newapid.desc[1]=buf[p+pos+3];
+ newapid.desc[2]=buf[p+pos+4];
+ newapid.desc[3]=0;
+ nolang=false;
+ // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDLANG is %s", newapid.desc);
+ } break;
+ case 0x56: {
+ type=3;
+ new_channelinfo.tpid=foundpid;
+ notfound=false;
+ } break;
+ };
+ pos+=2+buf[p+pos+1];
+ }
+ if (type==1) {
+ new_channelinfo.dpids.push_back(newapid);
+ new_channelinfo.numDPids++;
+ } else if (type==2) {
+ new_channelinfo.spids.push_back(newapid);
+ new_channelinfo.numSPids++;
+ }
+
+ } break;
+ default://TODO how about subtitles and second audio pids
+ break;
+ }
+ }
+
+ p += eslength; //skip es descriptor
+
+
+ }
+ bool audioPIDpresent=false; //Check if pids chnages
+ ULONG i;
+ for (i=0;i<channelinfo.numAPids;i++) {
+ if (aID == (int)channelinfo.apids[i].pid) {
+ audioPIDpresent=true;
+ }
+ }
+ for (i=0;i<channelinfo.numDPids && (! audioPIDpresent);i++) {
+ if (aID == (int)channelinfo.dpids[i].pid) {
+ audioPIDpresent=true;
+ }
+ }
+ if (! audioPIDpresent) {
+ bool found=false;
+ if (channelinfo.numAPids>0) {
+ int j=0;
+ while (j<channelinfo.numAPids && !found) {
+ if (Audio::getInstance()->streamTypeSupported(channelinfo.apids[j].type)) {
+ found =true;
+ setAID(channelinfo.apids[j].pid,0,channelinfo.apids[j].type);
+ }
+ j++;
+ }
+
+ }
+ if (channelinfo.numDPids>0 && !found) {
+ int j=0;
+ while (j<channelinfo.numDPids && !found) {
+ if (Audio::getInstance()->streamTypeSupported(channelinfo.dpids[j].type)) {
+ found =true;
+ setAID(channelinfo.dpids[j].pid,1,channelinfo.dpids[j].type);
+ }
+ i++;
+ }
+ }
+ }
+
+ channelinfo=new_channelinfo;
+
+
+ havechannelinfo=true;
+
+ return 1;
+ }
+
+
+
+
+ if (pid == vID)
+ {
+ if (vActive)
+ {
+ if (!parsed)
+ {
+ parseTSPacketDetails(vPacket);
+ parsed = true;
+ }
+ rc = submitPacket(vPacket);
+ }
+ vActive = true;
+ }
+ if (pid == aID)
+ {
+ if (aActive)
+ {
+ if (!parsed)
+ {
+ parseTSPacketDetails(aPacket);
+ parsed = true;
+ }
+ rc = submitPacket(aPacket);
+ }
+ aActive = true;
+ }
+ if (pid == subID)
+ {
+ if (subActive)
+ {
+ if (!parsed)
+ {
+ parseTSPacketDetails(subPacket);
+ parsed = true;
+ }
+ rc = submitPacket(subPacket);
+ }
+ subActive = true;
+ }
+ if (isteletextdecoded && pid == tID)
+ {
+ if (tActive)
+ {
+ if (!parsed)
+ {
+ parseTSPacketDetails(tPacket);
+ parsed = true;
+ }
+ rc = submitPacket(tPacket);
+ }
+ tActive = true;
+ }
+ if (rc == 0) return 0;
+
+ parsed = false;
+ if (pid == vID)
+ {
+ vPacket.init(PESTYPE_VID0);
+ buf += 6; datalen -= 6;
+ }
+ if (pid == aID)
+ {
+ switch (atype)
+ {
+ case 1:
+ aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);
+ break;
+ default:
+ case 0:
+ aPacket.init(PESTYPE_AUD0);
+ break;
+ }
+ buf += 6; datalen -= 6;
+ }
+ if (pid == subID)
+ {
+ subPacket.init(PESTYPE_PRIVATE_1);
+ subLength = (buf[4] << 8) + buf[5];
+ buf += 6; datalen -= 6;
+ }
+ if (isteletextdecoded && pid == tID)
+ {
+ tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);
+ buf += 6; datalen -= 6;
+ }
+ }
+
+ if ( (pid == vID && vActive) ||
+ (pid == aID && aActive) ||
+ (pid == tID && tActive) ||
+ (pid == subID && subActive) )
+ {
+ PESPacket* packet = NULL;
+ if (pid == vID) packet = &vPacket;
+ if (pid == aID) packet = &aPacket;
+ if (pid == subID) {
+ packet = &subPacket;
+ }
+ if (pid == tID) packet = &tPacket;
+ if (packet != NULL)
+ {
+ if (packet->write(buf, datalen) == 0)
+ { // Writing to packet failed. It has overflowed.
+ if (!parsed)
+ {
+ parseTSPacketDetails(*packet);
+ parsed = true;
+ }
+ if (submitPacket(*packet) == 0) return 0;
+ parsed = false;
+ packet->truncate();
+ packet->write((UCHAR*)"\200\000\000", 3);
+ packet->write(buf, datalen);
+ }
+ }
+ }
+
+ if (pid == subID && subActive && subPacket.getLength() == subLength)
+ {
+ parsePacketDetails(subPacket);
+Log::getInstance()->log("DEMUXERTS", Log::DEBUG, "SUBMITTING A SUBTITLE PACKET %d %x", subLength, subPacket.getSubstream());
+ submitPacket(subPacket);
+ subActive = false;
+ }
+
+ return 1;
+}
+
+ULONG DemuxerTS::getPacketNum()
+{
+ return packetNumber;
+}
+
+ULONG DemuxerTS::getFrameNumFromPTS(ULLONG pts)
+{
+ ULLONG difference = (1LL<<33);
+ ULONG ref_frame = 0;
+ int total = 0, actual = 0;
+ if (pts==0) return 0; //we are in startup
+ pts_map_mutex.Lock();
+ PTSMap::iterator iter = pts_map.begin();
+ while (iter != pts_map.end())
+ {
+ ++total;
+ //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts1 %lld pts2 %lld", pts, iter->pts);
+ if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE)
+ {
+ difference = 0;
+ ref_frame = iter->frame;
+ actual = total;
+ break;
+ }
+ ULLONG newdiff = PTSDifference(pts, iter->pts);
+ if (newdiff < difference)
+ {
+ difference = newdiff;
+ ref_frame = iter->frame;
+ actual = total;
+ }
+ ++iter;
+ }
+ if (total > 1 && actual == 1) // We are using the most recent PTS ref.
+ { // Delete the rest.
+ iter = pts_map.begin(); iter++;
+ pts_map.erase(iter, pts_map.end());
+ }
+ pts_map_mutex.Unlock();
+
+ //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts %lld deleted %d difference %lld", pts, total,difference);
+
+ if (difference == (1LL<<33))
+ return 0; // We cannot make sense of the pts
+ else
+ return ref_frame + difference * fps / 90000;
+}
+
+
+void DemuxerTS::parseTSPacketDetails(PESPacket &packet) // Only important stuff for paket counting reminas
+{
+ parsePacketDetails(packet);
+ if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 &&
+ packet.getPacketType() <= PESTYPE_AUDMAX)
+ {
+ packetNumber++;
+ }
+ UINT pictsinpacket=packet.countPictureHeaders(h264);
+
+ UINT numpicts=0;
+ /* if (!doubledframerate)
+ {*/
+ numpicts=pictsinpacket;
+ /* }
+ else
+ {
+ numpicts=(pictsinpacket+framereserve)>>1;
+ framereserve=(pictsinpacket+framereserve)%2;
+ }*/
+
+
+ if (frameCounting && numpicts &&
+ packet.getPacketType() >= PESTYPE_VID0 &&
+ packet.getPacketType() <= PESTYPE_VIDMAX)
+ {
+ frameNumber+=numpicts;
+ ULONG frame_num = frameNumber;
+ if ((h264 || packet.findSeqHeader(h264) > 1) && packet.hasPTS())
+ {
+ PTSMapEntry me;
+ pts_map_mutex.Lock();
+ if (pts_map.empty())
+ {
+ me.pts = packet.getPTS();
+ me.frame = frame_num;
+ pts_map_mutex.Unlock();
+ pts_map_mutex.Lock();
+ pts_map.push_front(me);
+ }
+ me = pts_map.front();
+ pts_map_mutex.Unlock();
+
+ //UINT fps = Video::getInstance()->getFPS();
+ double tfps=fps;
+ // if (doubledframerate) tfps*=2.;
+ long long pts_expected = me.pts + 90000LL*((long long)(((double)(frame_num - me.frame)) / tfps));
+
+ while (pts_expected < 0) pts_expected += (1LL<<33);
+ while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33);
+
+ // this was a workaround for a vdr bug, now fixed, for older recordings deleter the index file
+
+ /*
+ if (!doubledframerate
+ &&(abs((long long)PTSDistance(pts_expected, packet.getPTS())-1800) <= 1
+ || abs((long long)PTSDistance(pts_expected, packet.getPTS())-1501) <= 1)) {
+ doubledframerate=true; //Detected p50 or p60
+ }*/
+
+ if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump!
+ {
+ me.pts = packet.getPTS();
+ me.frame = frame_num;
+ pts_map_mutex.Lock();
+ pts_map.push_front(me);
+ pts_map_mutex.Unlock();
+ }
+ }
+ }
+}
+
+
+bool DemuxerTS::scanForVideo(UCHAR* buf, UINT len, bool &ish264)
+{
+ int pmtpidy=0;
+
+ while (len >= TS_SIZE)
+ {
+ if (*buf != TS_SIG) {buf++;len--; continue;}
+
+ //Pattern scanning won't work for ts
+
+
+ int datalen = TS_SIZE - 4;
+ int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];
+ UCHAR payload = buf[1] & 0x40;
+
+ if (buf[3] & 0x20) // Adaptation field is present
+ datalen -= (buf[4] + 1);
+
+ UCHAR* curbuf =buf+ (TS_SIZE - datalen);
+
+ if (payload) {
+ if (pid == 0x00) {//PAT, only take first program number, ignore the rest
+ int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12);
+ if ((pmtpid >> 13) != 0x07)
+ {
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13));
+ }
+ else
+ {
+ pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits
+ pmtpidy = pmtpid;
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMT pid%02x",pmtpid );
+ }
+
+ } else if (pid == pmtpidy) { //PMT
+ int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3);
+ //sectionlength += 4; //include header but subtract crc in the end...
+ int p = 13; //skip fixed part of pmt
+ while ( p < sectionlength) {
+ int streamtype = *(curbuf+p);
+ p++;
+ int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1);
+ p += 2; //skip ES Pid
+ int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1);
+ p += 2; //skip ES length
+ if ((foundpid >> 13) != 0x07)
+ {
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));
+ }
+ else
+ {
+ foundpid = foundpid & 0x1FFF; //clear upper 3 bits
+ // int pos=0; UNUSED?
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Pid found %02x type %02x",foundpid ,streamtype);
+ if (streamtype==1 || streamtype ==2) {
+ ish264=false;
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found Mpeg2 Video");
+ return true;
+ }
+ if (streamtype==0x1b) {
+ ish264=true;
+ Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found h264 Video");
+ return true;
+ }
+ }
+ p += eslength; //skip es descriptor
+ }
+ ish264=false;
+ return false;
+ }
+ }
+ len-=TS_SIZE;
+ buf+=TS_SIZE;
+ }
+ ish264=false;
+ return false;
+}
+
+UINT DemuxerTS::stripAudio(UCHAR* buf, UINT len) //it has to be adapted
+{
+ //This function strips all TS Headers and non video payload
+ UINT readpos=0;
+ UINT writepos=0;
+ PESPacket destpaket;
+ bool started=true;
+ while (readpos < len ) {
+ if (buf[readpos] != TS_SIG) {readpos++; continue;}
+ UINT oldreadpos=readpos;
+
+ int datalen = TS_SIZE - 4;
+ int pid = ( (buf[readpos+1] & 0x1F) << 8 ) | buf[readpos+2];
+ UCHAR payload = buf[readpos+1] & 0x40;
+ if (buf[readpos+3] & 0x20) { // Adaptation field is present
+ datalen -= (buf[readpos+4] + 1);
+ }
+ if (datalen < 0) {// Error in stream TODO log this
+ return 0;
+ }
+ if (datalen == 0) {// Null packet
+ readpos=oldreadpos+TS_SIZE;
+ continue;
+ }
+ readpos += (TS_SIZE - datalen);
+ UINT towrite=min(datalen,len-readpos);
+ if (pid == vID) {
+ if (payload) {
+ if (started) {
+ parsePacketDetails(destpaket);
+ memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());
+ writepos+=destpaket.getSize();
+ }
+ destpaket.init(PESTYPE_VID0);
+ readpos += 6; towrite -= 6;
+ started=true;
+ }
+
+ if (started) {
+ if (!destpaket.write(buf+readpos,towrite)) {
+ parsePacketDetails(destpaket);
+ memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());
+ writepos+=destpaket.getSize();
+ destpaket.truncate();
+ destpaket.write((UCHAR*)"\200\000\000", 3);
+ destpaket.write(buf+readpos,towrite);
+
+ }
+ }
+
+
+
+ }
+ readpos=oldreadpos+TS_SIZE;
+ }
+ parsePacketDetails(destpaket);
+ memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());
+ writepos+=destpaket.getSize();
+
+ return writepos;
+}
+
+
+