Fix for several TS-related problems.

This commit is contained in:
Thulinma 2016-07-05 17:06:15 +02:00
parent 7b0d3a9365
commit 9536ab1414
6 changed files with 148 additions and 93 deletions

View file

@ -18,6 +18,12 @@ namespace aac {
memcpy(data, _data, len); 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){ adts::adts(const adts & rhs){
data = NULL; data = NULL;
len = 0; len = 0;
@ -56,7 +62,7 @@ namespace aac {
} }
unsigned long adts::getFrequency(){ unsigned long adts::getFrequency(){
if (!data || !len){ if (!data || len < 3){
return 0; return 0;
} }
switch(getFrequencyIndex()){ switch(getFrequencyIndex()){
@ -99,14 +105,25 @@ namespace aac {
} }
unsigned long adts::getPayloadSize(){ unsigned long adts::getPayloadSize(){
if (!data || !len){ if (!data || len < 6){
return 0; 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(){ unsigned long adts::getSampleCount(){
if (!data || !len){ if (!data || len < 7){
return 0; return 0;
} }
return ((data[6] & 0x03) + 1) * 1024;//Number of samples in this frame * 1024 return ((data[6] & 0x03) + 1) * 1024;//Number of samples in this frame * 1024

View file

@ -8,6 +8,7 @@ namespace aac {
adts(const adts & rhs); adts(const adts & rhs);
~adts(); ~adts();
adts& operator = (const adts & rhs); adts& operator = (const adts & rhs);
bool sameHeader(const adts & rhs) const;
unsigned long getAACProfile(); unsigned long getAACProfile();
unsigned long getFrequencyIndex(); unsigned long getFrequencyIndex();
unsigned long getFrequency(); unsigned long getFrequency();

View file

@ -56,8 +56,10 @@ namespace TS {
} }
int tid = newPack.getPID(); int tid = newPack.getPID();
pesStreams[tid].push_back(newPack); if ((pidToCodec.count(tid) || tid == 0 || newPack.isPMT()) && (pesStreams[tid].size() || newPack.getUnitStart())){
pesPositions[tid].push_back(bytePos); pesStreams[tid].push_back(newPack);
pesPositions[tid].push_back(bytePos);
}
if (threaded){ if (threaded){
globalSem.post(); globalSem.post();
@ -102,6 +104,7 @@ namespace TS {
globalSem.wait(); globalSem.wait();
} }
associationTable = trackPackets.back(); associationTable = trackPackets.back();
associationTable.parsePIDs();
lastPAT = Util::bootSecs(); lastPAT = Util::bootSecs();
if (threaded){ if (threaded){
globalSem.post(); globalSem.post();
@ -124,6 +127,11 @@ namespace TS {
return; return;
} }
//Ignore conditional access packets. We don't care.
if (tid == 1){
return;
}
//Handle PMT packets //Handle PMT packets
if (pmtTracks.count(tid)){ if (pmtTracks.count(tid)){
///\todo Keep track of updates in PMT instead of keeping only the last PMT per program as a reference ///\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){ switch(sType){
case H264: case H264:
case AAC: case AAC:
case HEVC:
case H265: case H265:
case AC3: case AC3:
case ID3: case ID3:
@ -272,6 +279,7 @@ namespace TS {
} }
void Stream::parsePES(unsigned long tid){ void Stream::parsePES(unsigned long tid){
if (!pidToCodec.count(tid)){return;}//skip unknown codecs
if (threaded){ if (threaded){
globalSem.wait(); globalSem.wait();
} }
@ -308,11 +316,13 @@ namespace TS {
paySize += curPack->getPayloadLength(); paySize += curPack->getPayloadLength();
curPack++; curPack++;
} }
VERYHIGH_MSG("Parsing PES for track %lu, length %i", tid, paySize);
char * payload = (char*)malloc(paySize); char * payload = (char*)malloc(paySize);
paySize = 0; paySize = 0;
curPack = inStream.begin(); curPack = inStream.begin();
int lastCtr = curPack->getContinuityCounter() - 1; int lastCtr = curPack->getContinuityCounter() - 1;
for (int i = 0; i < packNum; i++){ for (int i = 0; i < packNum; i++){
if (curPack->getContinuityCounter() == lastCtr){curPack++; continue;}
if (curPack->getContinuityCounter() - lastCtr != 1 && curPack->getContinuityCounter()){ if (curPack->getContinuityCounter() - lastCtr != 1 && curPack->getContinuityCounter()){
INFO_MSG("Parsing a pes on track %d, missed %d packets", tid, curPack->getContinuityCounter() - lastCtr - 1); 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){ if (paySize - offset - pesOffset < realPayloadSize){
INFO_MSG("Not enough data left on track %lu.", tid); INFO_MSG("Not enough data left on track %lu.", tid);
break; break;
@ -391,12 +417,14 @@ namespace TS {
globalSem.wait(); globalSem.wait();
} }
while (offsetInPes < realPayloadSize){ while (offsetInPes < realPayloadSize){
outPackets[tid].push_back(DTSC::Packet());
aac::adts adtsPack(pesPayload + offsetInPes, realPayloadSize - offsetInPes); aac::adts adtsPack(pesPayload + offsetInPes, realPayloadSize - offsetInPes);
if (!adtsInfo.count(tid)){ if (adtsPack.getFrequency() && adtsPack.getPayloadSize()){
adtsInfo[tid] = adtsPack; 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(); samplesRead += adtsPack.getSampleCount();
offsetInPes += adtsPack.getHeaderSize() + adtsPack.getPayloadSize(); offsetInPes += adtsPack.getHeaderSize() + adtsPack.getPayloadSize();
} }
@ -414,7 +442,7 @@ namespace TS {
globalSem.post(); globalSem.post();
} }
} }
if (pidToCodec[tid] == H264 || pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ if (pidToCodec[tid] == H264 || pidToCodec[tid] == H265){
//Convert from annex b //Convert from annex b
char * parsedData = (char*)malloc(realPayloadSize * 2); char * parsedData = (char*)malloc(realPayloadSize * 2);
bool isKeyFrame = false; bool isKeyFrame = false;
@ -423,7 +451,7 @@ namespace TS {
if (pidToCodec[tid] == H264) { if (pidToCodec[tid] == H264) {
nalInfo = h264::analysePackets(parsedData, parsedSize); nalInfo = h264::analysePackets(parsedData, parsedSize);
} }
if (pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ if (pidToCodec[tid] == H265){
nalInfo = h265::analysePackets(parsedData, parsedSize); nalInfo = h265::analysePackets(parsedData, parsedSize);
} }
int dataOffset = 0; int dataOffset = 0;
@ -457,7 +485,7 @@ namespace TS {
default: break; default: break;
} }
} }
if (pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ if (pidToCodec[tid] == H265){
switch (it->nalType){ switch (it->nalType){
case 2: case 3: //TSA Picture case 2: case 3: //TSA Picture
case 4: case 5: //STSA Picture case 4: case 5: //STSA Picture
@ -590,66 +618,77 @@ namespace TS {
if (tid && it->first != tid){ if (tid && it->first != tid){
continue; continue;
} }
if (!meta.tracks.count(it->first) && it->second == H264){ if (meta.tracks.count(it->first) && meta.tracks[it->first].codec.size()){continue;}
if (!spsInfo.count(it->first) || !ppsInfo.count(it->first)){ switch (it->second){
continue; 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"; break;
meta.tracks[it->first].codec = "H264"; case H265: {
meta.tracks[it->first].trackID = it->first; if (!hevcInfo.count(it->first) || !hevcInfo[it->first].haveRequired()){
std::string tmpBuffer = spsInfo[it->first]; MEDIUM_MSG("Aborted meta fill for hevc track %lu: no info nal unit", it->first);
h264::sequenceParameterSet sps(spsInfo[it->first].data(), spsInfo[it->first].size()); continue;
h264::SPSMeta spsChar = sps.getCharacteristics(); }
meta.tracks[it->first].width = spsChar.width; meta.tracks[it->first].type = "video";
meta.tracks[it->first].height = spsChar.height; meta.tracks[it->first].codec = "HEVC";
meta.tracks[it->first].fpks = spsChar.fps * 1000; meta.tracks[it->first].trackID = it->first;
MP4::AVCC avccBox; meta.tracks[it->first].init = hevcInfo[it->first].generateHVCC();
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;
} }
meta.tracks[it->first].type = "video"; break;
meta.tracks[it->first].codec = "HEVC"; case ID3: {
meta.tracks[it->first].trackID = it->first; meta.tracks[it->first].type = "meta";
meta.tracks[it->first].init = hevcInfo[it->first].generateHVCC(); meta.tracks[it->first].codec = "ID3";
} meta.tracks[it->first].trackID = it->first;
if (!meta.tracks.count(it->first) && it->second == ID3){ meta.tracks[it->first].init = metaInit[it->first];
meta.tracks[it->first].type = "meta"; }
meta.tracks[it->first].codec = "ID3"; break;
meta.tracks[it->first].trackID = it->first; case AC3: {
meta.tracks[it->first].init = metaInit[it->first]; meta.tracks[it->first].type = "audio";
} meta.tracks[it->first].codec = "AC3";
if (!meta.tracks.count(it->first) && it->second == AC3){ meta.tracks[it->first].trackID = it->first;
meta.tracks[it->first].type = "audio"; meta.tracks[it->first].size = 16;
meta.tracks[it->first].codec = "AC3"; ///\todo Fix these 2 values
meta.tracks[it->first].trackID = it->first; meta.tracks[it->first].rate = 0;
meta.tracks[it->first].size = 16; meta.tracks[it->first].channels = 0;
///\todo Fix these 2 values }
meta.tracks[it->first].rate = 0; break;
meta.tracks[it->first].channels = 0; case AAC: {
} meta.tracks[it->first].type = "audio";
if (!meta.tracks.count(it->first) && it->second == AAC){ meta.tracks[it->first].codec = "AAC";
meta.tracks[it->first].type = "audio"; meta.tracks[it->first].trackID = it->first;
meta.tracks[it->first].codec = "AAC"; meta.tracks[it->first].size = 16;
meta.tracks[it->first].trackID = it->first; meta.tracks[it->first].rate = adtsInfo[it->first].getFrequency();
meta.tracks[it->first].size = 16; meta.tracks[it->first].channels = adtsInfo[it->first].getChannelCount();
meta.tracks[it->first].rate = adtsInfo[it->first].getFrequency(); char audioInit[2];//5 bits object type, 4 bits frequency index, 4 bits channel index
meta.tracks[it->first].channels = adtsInfo[it->first].getChannelCount(); audioInit[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) | ((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1);
char audioInit[2];//5 bits object type, 4 bits frequency index, 4 bits channel index audioInit[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) | ((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3);
audioInit[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) | ((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1); meta.tracks[it->first].init = std::string(audioInit, 2);
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){ if (threaded){
globalSem.post(); globalSem.post();
@ -679,7 +718,6 @@ namespace TS {
switch(entry.getStreamType()){ switch(entry.getStreamType()){
case H264: case H264:
case AAC: case AAC:
case HEVC:
case H265: case H265:
case AC3: case AC3:
case ID3: case ID3:

View file

@ -13,7 +13,6 @@ namespace TS {
AAC = 0x0F, AAC = 0x0F,
AC3 = 0x81, AC3 = 0x81,
MP3 = 0x03, MP3 = 0x03,
HEVC = 0x06,
H265 = 0x24, H265 = 0x24,
ID3 = 0x15 ID3 = 0x15
}; };

View file

@ -11,6 +11,7 @@
#include <sstream> #include <sstream>
#include <signal.h> #include <signal.h>
#include <mist/ts_packet.h> #include <mist/ts_packet.h>
#include <mist/bitfields.h>
#include <mist/config.h> #include <mist/config.h>
@ -29,10 +30,10 @@ namespace Analysers {
known = true; known = true;
} }
if (!known){ if (!known){
res << " [Unknown stream ID]"; res << " [Unknown stream ID: " << (int)d[3] << "]";
} }
if (d[0] != 0 || d[1] != 0 || d[2] != 1){ 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 (known){
if ((d[6] & 0xC0) != 0x80){ if ((d[6] & 0xC0) != 0x80){
@ -56,12 +57,20 @@ namespace Analysers {
res << " [Copy]"; res << " [Copy]";
} }
int timeFlags = ((d[7] & 0xC0) >> 6);
if (timeFlags == 2){
headSize += 5;
}
if (timeFlags == 3){
headSize += 10;
}
if (d[7] & 0x20){ if (d[7] & 0x20){
res << " [ESCR present, not decoded!]"; res << " [ESCR present, not decoded!]";
headSize += 6; headSize += 6;
} }
if (d[7] & 0x10){ 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; headSize += 3;
} }
if (d[7] & 0x08){ if (d[7] & 0x08){
@ -80,13 +89,6 @@ namespace Analysers {
res << " [Extension present, not decoded!]"; res << " [Extension present, not decoded!]";
headSize += 0; /// \todo Implement this. Complicated field, bah. 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){ if (d[8] != headSize){
res << " [Padding: " << ((int)d[8] - headSize) << "b]"; res << " [Padding: " << ((int)d[8] - headSize) << "b]";
} }
@ -145,13 +147,13 @@ namespace Analysers {
std::cout << printPES(payloads[packet.getPID()], packet.getPID(), detailLevel); std::cout << printPES(payloads[packet.getPID()], packet.getPID(), detailLevel);
payloads.erase(packet.getPID()); payloads.erase(packet.getPID());
} }
if (detailLevel < 2){ if (detailLevel >= 3 || !packet.getPID() || packet.isPMT()){
std::stringstream nul; if (packet.getPID() == 0){
nul << packet.toPrettyString(0, detailLevel); ((TS::ProgramAssociationTable*)&packet)->parsePIDs();
}else{ }
std::cout << packet.toPrettyString(0, detailLevel); 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()); payloads[packet.getPID()].append(packet.getPayload(), packet.getPayloadLength());
} }
} }

View file

@ -245,18 +245,16 @@ namespace Mist {
TS::Packet packet;//to analyse and extract data TS::Packet packet;//to analyse and extract data
fseek(inFile, 0, SEEK_SET);//seek to beginning fseek(inFile, 0, SEEK_SET);//seek to beginning
bool first = true;
long long int lastBpos = 0; long long int lastBpos = 0;
while (packet.FromFile(inFile) && !feof(inFile)) { while (packet.FromFile(inFile) && !feof(inFile)) {
tsStream.parse(packet, lastBpos); tsStream.parse(packet, lastBpos);
lastBpos = ftell(inFile); lastBpos = ftell(inFile);
while (tsStream.hasPacketOnEachTrack()) { while (tsStream.hasPacketOnEachTrack()) {
if (first) {
tsStream.initializeMetadata(myMeta);
first = false;
}
DTSC::Packet headerPack; DTSC::Packet headerPack;
tsStream.getEarliestPacket(headerPack); tsStream.getEarliestPacket(headerPack);
if (!myMeta.tracks.count(headerPack.getTrackId()) || !myMeta.tracks[headerPack.getTrackId()].codec.size()) {
tsStream.initializeMetadata(myMeta, headerPack.getTrackId());
}
myMeta.update(headerPack); myMeta.update(headerPack);
} }