diff --git a/lib/adts.cpp b/lib/adts.cpp index cc3d7f14..ee892359 100644 --- a/lib/adts.cpp +++ b/lib/adts.cpp @@ -18,6 +18,12 @@ namespace aac { memcpy(data, _data, len); } + + bool adts::sameHeader(const adts & rhs) const { + if (len < 7 || rhs.len < 7){return false;} + return (memcmp(data, rhs.data, 7) == 0); + } + adts::adts(const adts & rhs){ data = NULL; len = 0; @@ -56,7 +62,7 @@ namespace aac { } unsigned long adts::getFrequency(){ - if (!data || !len){ + if (!data || len < 3){ return 0; } switch(getFrequencyIndex()){ @@ -99,14 +105,25 @@ namespace aac { } unsigned long adts::getPayloadSize(){ - if (!data || !len){ + if (!data || len < 6){ return 0; } - return (((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] >> 5) & 0x07)) - getHeaderSize(); + unsigned long ret = (((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] >> 5) & 0x07)); + if (!ret){return ret;}//catch zero length + if (ret >= getHeaderSize()){ + ret -= getHeaderSize(); + }else{ + return 0;//catch size less than header size (corrupt data) + } + if (len < ret + getHeaderSize() ){ + ret = len - getHeaderSize(); + //catch size less than length (corrupt data) + } + return ret; } unsigned long adts::getSampleCount(){ - if (!data || !len){ + if (!data || len < 7){ return 0; } return ((data[6] & 0x03) + 1) * 1024;//Number of samples in this frame * 1024 diff --git a/lib/adts.h b/lib/adts.h index 533460d1..25b273b2 100644 --- a/lib/adts.h +++ b/lib/adts.h @@ -8,6 +8,7 @@ namespace aac { adts(const adts & rhs); ~adts(); adts& operator = (const adts & rhs); + bool sameHeader(const adts & rhs) const; unsigned long getAACProfile(); unsigned long getFrequencyIndex(); unsigned long getFrequency(); diff --git a/lib/ts_stream.cpp b/lib/ts_stream.cpp index e92d92fa..51ad75ae 100644 --- a/lib/ts_stream.cpp +++ b/lib/ts_stream.cpp @@ -56,8 +56,10 @@ namespace TS { } int tid = newPack.getPID(); - pesStreams[tid].push_back(newPack); - pesPositions[tid].push_back(bytePos); + if ((pidToCodec.count(tid) || tid == 0 || newPack.isPMT()) && (pesStreams[tid].size() || newPack.getUnitStart())){ + pesStreams[tid].push_back(newPack); + pesPositions[tid].push_back(bytePos); + } if (threaded){ globalSem.post(); @@ -102,6 +104,7 @@ namespace TS { globalSem.wait(); } associationTable = trackPackets.back(); + associationTable.parsePIDs(); lastPAT = Util::bootSecs(); if (threaded){ globalSem.post(); @@ -124,6 +127,11 @@ namespace TS { return; } + //Ignore conditional access packets. We don't care. + if (tid == 1){ + return; + } + //Handle PMT packets if (pmtTracks.count(tid)){ ///\todo Keep track of updates in PMT instead of keeping only the last PMT per program as a reference @@ -142,7 +150,6 @@ namespace TS { switch(sType){ case H264: case AAC: - case HEVC: case H265: case AC3: case ID3: @@ -272,6 +279,7 @@ namespace TS { } void Stream::parsePES(unsigned long tid){ + if (!pidToCodec.count(tid)){return;}//skip unknown codecs if (threaded){ globalSem.wait(); } @@ -308,11 +316,13 @@ namespace TS { paySize += curPack->getPayloadLength(); curPack++; } + VERYHIGH_MSG("Parsing PES for track %lu, length %i", tid, paySize); char * payload = (char*)malloc(paySize); paySize = 0; curPack = inStream.begin(); int lastCtr = curPack->getContinuityCounter() - 1; for (int i = 0; i < packNum; i++){ + if (curPack->getContinuityCounter() == lastCtr){curPack++; continue;} if (curPack->getContinuityCounter() - lastCtr != 1 && curPack->getContinuityCounter()){ INFO_MSG("Parsing a pes on track %d, missed %d packets", tid, curPack->getContinuityCounter() - lastCtr - 1); } @@ -375,6 +385,22 @@ namespace TS { } } + if (pesHeader[7] & 0x20){ //ESCR - ignored + pesOffset += 6; + } + if (pesHeader[7] & 0x10){ //ESR - ignored + pesOffset += 3; + } + if (pesHeader[7] & 0x08){ //trick mode - ignored + pesOffset += 1; + } + if (pesHeader[7] & 0x04){//additional copy - ignored + pesOffset += 1; + } + if (pesHeader[7] & 0x02){ //crc - ignored + pesOffset += 2; + } + if (paySize - offset - pesOffset < realPayloadSize){ INFO_MSG("Not enough data left on track %lu.", tid); break; @@ -391,12 +417,14 @@ namespace TS { globalSem.wait(); } while (offsetInPes < realPayloadSize){ - outPackets[tid].push_back(DTSC::Packet()); aac::adts adtsPack(pesPayload + offsetInPes, realPayloadSize - offsetInPes); - if (!adtsInfo.count(tid)){ - adtsInfo[tid] = adtsPack; + if (adtsPack.getFrequency() && adtsPack.getPayloadSize()){ + if (!adtsInfo.count(tid) || !adtsInfo[tid].sameHeader(adtsPack)){ + adtsInfo[tid] = adtsPack; + } + outPackets[tid].push_back(DTSC::Packet()); + outPackets[tid].back().genericFill(timeStamp + ((samplesRead * 1000) / adtsPack.getFrequency()), timeOffset, tid, adtsPack.getPayload(), adtsPack.getPayloadSize(), bPos, 0); } - outPackets[tid].back().genericFill(timeStamp + ((samplesRead * 1000) / adtsPack.getFrequency()), timeOffset, tid, adtsPack.getPayload(), adtsPack.getPayloadSize(), bPos, 0); samplesRead += adtsPack.getSampleCount(); offsetInPes += adtsPack.getHeaderSize() + adtsPack.getPayloadSize(); } @@ -414,7 +442,7 @@ namespace TS { globalSem.post(); } } - if (pidToCodec[tid] == H264 || pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ + if (pidToCodec[tid] == H264 || pidToCodec[tid] == H265){ //Convert from annex b char * parsedData = (char*)malloc(realPayloadSize * 2); bool isKeyFrame = false; @@ -423,7 +451,7 @@ namespace TS { if (pidToCodec[tid] == H264) { nalInfo = h264::analysePackets(parsedData, parsedSize); } - if (pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ + if (pidToCodec[tid] == H265){ nalInfo = h265::analysePackets(parsedData, parsedSize); } int dataOffset = 0; @@ -457,7 +485,7 @@ namespace TS { default: break; } } - if (pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ + if (pidToCodec[tid] == H265){ switch (it->nalType){ case 2: case 3: //TSA Picture case 4: case 5: //STSA Picture @@ -590,66 +618,77 @@ namespace TS { if (tid && it->first != tid){ continue; } - if (!meta.tracks.count(it->first) && it->second == H264){ - if (!spsInfo.count(it->first) || !ppsInfo.count(it->first)){ - continue; + if (meta.tracks.count(it->first) && meta.tracks[it->first].codec.size()){continue;} + switch (it->second){ + case H264: { + if (!spsInfo.count(it->first) || !ppsInfo.count(it->first)){ + MEDIUM_MSG("Aborted meta fill for h264 track %lu: no SPS/PPS", it->first); + continue; + } + meta.tracks[it->first].type = "video"; + meta.tracks[it->first].codec = "H264"; + meta.tracks[it->first].trackID = it->first; + std::string tmpBuffer = spsInfo[it->first]; + h264::sequenceParameterSet sps(spsInfo[it->first].data(), spsInfo[it->first].size()); + h264::SPSMeta spsChar = sps.getCharacteristics(); + meta.tracks[it->first].width = spsChar.width; + meta.tracks[it->first].height = spsChar.height; + meta.tracks[it->first].fpks = spsChar.fps * 1000; + MP4::AVCC avccBox; + avccBox.setVersion(1); + avccBox.setProfile(spsInfo[it->first][1]); + avccBox.setCompatibleProfiles(spsInfo[it->first][2]); + avccBox.setLevel(spsInfo[it->first][3]); + avccBox.setSPSNumber(1); + avccBox.setSPS(spsInfo[it->first]); + avccBox.setPPSNumber(1); + avccBox.setPPS(ppsInfo[it->first]); + meta.tracks[it->first].init = std::string(avccBox.payload(), avccBox.payloadSize()); } - meta.tracks[it->first].type = "video"; - meta.tracks[it->first].codec = "H264"; - meta.tracks[it->first].trackID = it->first; - std::string tmpBuffer = spsInfo[it->first]; - h264::sequenceParameterSet sps(spsInfo[it->first].data(), spsInfo[it->first].size()); - h264::SPSMeta spsChar = sps.getCharacteristics(); - meta.tracks[it->first].width = spsChar.width; - meta.tracks[it->first].height = spsChar.height; - meta.tracks[it->first].fpks = spsChar.fps * 1000; - MP4::AVCC avccBox; - avccBox.setVersion(1); - avccBox.setProfile(spsInfo[it->first][1]); - avccBox.setCompatibleProfiles(spsInfo[it->first][2]); - avccBox.setLevel(spsInfo[it->first][3]); - avccBox.setSPSNumber(1); - avccBox.setSPS(spsInfo[it->first]); - avccBox.setPPSNumber(1); - avccBox.setPPS(ppsInfo[it->first]); - meta.tracks[it->first].init = std::string(avccBox.payload(), avccBox.payloadSize()); - } - if (!meta.tracks.count(it->first) && (it->second == HEVC || it->second == H265)){ - if (!hevcInfo.count(it->first) || !hevcInfo[it->first].haveRequired()){ - continue; + break; + case H265: { + if (!hevcInfo.count(it->first) || !hevcInfo[it->first].haveRequired()){ + MEDIUM_MSG("Aborted meta fill for hevc track %lu: no info nal unit", it->first); + continue; + } + meta.tracks[it->first].type = "video"; + meta.tracks[it->first].codec = "HEVC"; + meta.tracks[it->first].trackID = it->first; + meta.tracks[it->first].init = hevcInfo[it->first].generateHVCC(); } - meta.tracks[it->first].type = "video"; - meta.tracks[it->first].codec = "HEVC"; - meta.tracks[it->first].trackID = it->first; - meta.tracks[it->first].init = hevcInfo[it->first].generateHVCC(); - } - if (!meta.tracks.count(it->first) && it->second == ID3){ - meta.tracks[it->first].type = "meta"; - meta.tracks[it->first].codec = "ID3"; - meta.tracks[it->first].trackID = it->first; - meta.tracks[it->first].init = metaInit[it->first]; - } - if (!meta.tracks.count(it->first) && it->second == AC3){ - meta.tracks[it->first].type = "audio"; - meta.tracks[it->first].codec = "AC3"; - meta.tracks[it->first].trackID = it->first; - meta.tracks[it->first].size = 16; - ///\todo Fix these 2 values - meta.tracks[it->first].rate = 0; - meta.tracks[it->first].channels = 0; - } - if (!meta.tracks.count(it->first) && it->second == AAC){ - meta.tracks[it->first].type = "audio"; - meta.tracks[it->first].codec = "AAC"; - meta.tracks[it->first].trackID = it->first; - meta.tracks[it->first].size = 16; - meta.tracks[it->first].rate = adtsInfo[it->first].getFrequency(); - meta.tracks[it->first].channels = adtsInfo[it->first].getChannelCount(); - char audioInit[2];//5 bits object type, 4 bits frequency index, 4 bits channel index - audioInit[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) | ((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1); - audioInit[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) | ((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3); - meta.tracks[it->first].init = std::string(audioInit, 2); + break; + case ID3: { + meta.tracks[it->first].type = "meta"; + meta.tracks[it->first].codec = "ID3"; + meta.tracks[it->first].trackID = it->first; + meta.tracks[it->first].init = metaInit[it->first]; + } + break; + case AC3: { + meta.tracks[it->first].type = "audio"; + meta.tracks[it->first].codec = "AC3"; + meta.tracks[it->first].trackID = it->first; + meta.tracks[it->first].size = 16; + ///\todo Fix these 2 values + meta.tracks[it->first].rate = 0; + meta.tracks[it->first].channels = 0; + } + break; + case AAC: { + meta.tracks[it->first].type = "audio"; + meta.tracks[it->first].codec = "AAC"; + meta.tracks[it->first].trackID = it->first; + meta.tracks[it->first].size = 16; + meta.tracks[it->first].rate = adtsInfo[it->first].getFrequency(); + meta.tracks[it->first].channels = adtsInfo[it->first].getChannelCount(); + char audioInit[2];//5 bits object type, 4 bits frequency index, 4 bits channel index + audioInit[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) | ((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1); + audioInit[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) | ((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3); + meta.tracks[it->first].init = std::string(audioInit, 2); + } + break; } + MEDIUM_MSG("Initialized track %lu as %s %s", it->first, meta.tracks[it->first].codec.c_str(), meta.tracks[it->first].type.c_str()); } if (threaded){ globalSem.post(); @@ -679,7 +718,6 @@ namespace TS { switch(entry.getStreamType()){ case H264: case AAC: - case HEVC: case H265: case AC3: case ID3: diff --git a/lib/ts_stream.h b/lib/ts_stream.h index a93aba93..e023e73a 100644 --- a/lib/ts_stream.h +++ b/lib/ts_stream.h @@ -13,7 +13,6 @@ namespace TS { AAC = 0x0F, AC3 = 0x81, MP3 = 0x03, - HEVC = 0x06, H265 = 0x24, ID3 = 0x15 }; diff --git a/src/analysers/ts_analyser.cpp b/src/analysers/ts_analyser.cpp index 71181965..6b75fd1e 100755 --- a/src/analysers/ts_analyser.cpp +++ b/src/analysers/ts_analyser.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -29,10 +30,10 @@ namespace Analysers { known = true; } if (!known){ - res << " [Unknown stream ID]"; + res << " [Unknown stream ID: " << (int)d[3] << "]"; } if (d[0] != 0 || d[1] != 0 || d[2] != 1){ - res << " [!INVALID START CODE!]"; + res << " [!!! INVALID START CODE: " << (int)d[0] << " " << (int)d[1] << " " << (int)d[2] << " ]"; } if (known){ if ((d[6] & 0xC0) != 0x80){ @@ -56,12 +57,20 @@ namespace Analysers { res << " [Copy]"; } + int timeFlags = ((d[7] & 0xC0) >> 6); + if (timeFlags == 2){ + headSize += 5; + } + if (timeFlags == 3){ + headSize += 10; + } if (d[7] & 0x20){ res << " [ESCR present, not decoded!]"; headSize += 6; } if (d[7] & 0x10){ - res << " [ESR present, not decoded!]"; + uint32_t es_rate = (Bit::btoh24(d.data()+9+headSize) & 0x7FFFFF) >> 1; + res << " [ESR: " << (es_rate * 50) / 1024 << " KiB/s]"; headSize += 3; } if (d[7] & 0x08){ @@ -80,13 +89,6 @@ namespace Analysers { res << " [Extension present, not decoded!]"; headSize += 0; /// \todo Implement this. Complicated field, bah. } - int timeFlags = ((d[7] & 0xC0) >> 6); - if (timeFlags == 2){ - headSize += 5; - } - if (timeFlags == 3){ - headSize += 10; - } if (d[8] != headSize){ res << " [Padding: " << ((int)d[8] - headSize) << "b]"; } @@ -145,13 +147,13 @@ namespace Analysers { std::cout << printPES(payloads[packet.getPID()], packet.getPID(), detailLevel); payloads.erase(packet.getPID()); } - if (detailLevel < 2){ - std::stringstream nul; - nul << packet.toPrettyString(0, detailLevel); - }else{ + if (detailLevel >= 3 || !packet.getPID() || packet.isPMT()){ + if (packet.getPID() == 0){ + ((TS::ProgramAssociationTable*)&packet)->parsePIDs(); + } std::cout << packet.toPrettyString(0, detailLevel); } - if (packet.getPID() && !packet.isPMT()){ + if (packet.getPID() && !packet.isPMT() && (payloads[packet.getPID()].size() || packet.getUnitStart())){ payloads[packet.getPID()].append(packet.getPayload(), packet.getPayloadLength()); } } diff --git a/src/input/input_ts.cpp b/src/input/input_ts.cpp index 72578b21..2ae05ffc 100755 --- a/src/input/input_ts.cpp +++ b/src/input/input_ts.cpp @@ -245,18 +245,16 @@ namespace Mist { TS::Packet packet;//to analyse and extract data fseek(inFile, 0, SEEK_SET);//seek to beginning - bool first = true; long long int lastBpos = 0; while (packet.FromFile(inFile) && !feof(inFile)) { tsStream.parse(packet, lastBpos); lastBpos = ftell(inFile); while (tsStream.hasPacketOnEachTrack()) { - if (first) { - tsStream.initializeMetadata(myMeta); - first = false; - } DTSC::Packet headerPack; tsStream.getEarliestPacket(headerPack); + if (!myMeta.tracks.count(headerPack.getTrackId()) || !myMeta.tracks[headerPack.getTrackId()].codec.size()) { + tsStream.initializeMetadata(myMeta, headerPack.getTrackId()); + } myMeta.update(headerPack); }