From 35b2dd6bee33f23267f59b0ea9043ea17001a703 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 5 Nov 2015 16:56:50 +0100 Subject: [PATCH] Better working TS input, Pro side. Code by Erik Zandvliet. --- CMakeLists.txt | 2 + lib/adts.cpp | 5 + lib/encryption.cpp | 2 +- lib/h265.cpp | 342 ++++++++++++++++++++++++++++ lib/h265.h | 42 ++++ lib/mp4_generic.cpp | 43 +++- lib/mp4_generic.h | 1 + lib/ts_packet.cpp | 2 +- lib/ts_stream.cpp | 175 +++++++++----- lib/ts_stream.h | 10 +- src/analysers/tsstream_analyser.cpp | 5 +- src/input/input.cpp | 11 +- src/input/input_ts.cpp | 59 ++++- src/input/input_ts.h | 4 + src/output/output_hss.cpp | 2 +- 15 files changed, 627 insertions(+), 78 deletions(-) create mode 100644 lib/h265.cpp create mode 100644 lib/h265.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 46e57def..63dd42bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ set(libHeaders ${SOURCE_DIR}/lib/encryption.h ${SOURCE_DIR}/lib/flv_tag.h ${SOURCE_DIR}/lib/h264.h + ${SOURCE_DIR}/lib/h265.h ${SOURCE_DIR}/lib/http_parser.h ${SOURCE_DIR}/lib/json.h ${SOURCE_DIR}/lib/mp4_adobe.h @@ -169,6 +170,7 @@ set(libSources ${SOURCE_DIR}/lib/encryption.cpp ${SOURCE_DIR}/lib/flv_tag.cpp ${SOURCE_DIR}/lib/h264.cpp + ${SOURCE_DIR}/lib/h265.cpp ${SOURCE_DIR}/lib/http_parser.cpp ${SOURCE_DIR}/lib/json.cpp ${SOURCE_DIR}/lib/mp4_adobe.cpp diff --git a/lib/adts.cpp b/lib/adts.cpp index 21af9707..cc3d7f14 100644 --- a/lib/adts.cpp +++ b/lib/adts.cpp @@ -19,10 +19,15 @@ namespace aac { } adts::adts(const adts & rhs){ + data = NULL; + len = 0; *this = rhs; } adts& adts::operator = (const adts & rhs){ + if (data){ + free(data); + } len = rhs.len; data = (char*)malloc(len); memcpy(data, rhs.data, len); diff --git a/lib/encryption.cpp b/lib/encryption.cpp index 94a03dd5..5e5b6df9 100644 --- a/lib/encryption.cpp +++ b/lib/encryption.cpp @@ -129,7 +129,7 @@ namespace Encryption { int pos = 0; - std::deque nalSizes = h264::parseNalSizes(thisPack); + std::deque nalSizes = nalu::parseNalSizes(thisPack); for (std::deque::iterator it = nalSizes.begin(); it != nalSizes.end(); it++) { int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16 int clear = *it - encrypted; diff --git a/lib/h265.cpp b/lib/h265.cpp new file mode 100644 index 00000000..b987123b --- /dev/null +++ b/lib/h265.cpp @@ -0,0 +1,342 @@ +#include "h265.h" +#include "bitfields.h" +#include "defines.h" + +namespace h265 { + std::deque analysePackets(const char * data, unsigned long len){ + std::deque res; + + int offset = 0; + while (offset < len){ + nalu::nalData entry; + entry.nalSize = Bit::btohl(data + offset); + entry.nalType = ((data + offset)[4] & 0x7E) >> 1; + res.push_back(entry); + offset += entry.nalSize + 4; + } + return res; + } + + initData::initData() {} + + void initData::addUnit(char * data) { + unsigned long nalSize = Bit::btohl(data); + unsigned long nalType = (data[4] & 0x7E) >> 1; + switch (nalType) { + case 32: //vps + case 33: //sps + case 34: //pps + nalUnits[nalType].insert(std::string(data + 4, nalSize)); + } + } + + bool initData::haveRequired() { + return (nalUnits.count(32) && nalUnits.count(33) && nalUnits.count(34)); + } + + std::string initData::generateHVCC(){ + MP4::HVCC hvccBox; + hvccBox.setConfigurationVersion(1); + hvccBox.setParallelismType(0); + std::set::iterator nalIt; + + //We first loop over all VPS Units + for (nalIt = nalUnits[32].begin(); nalIt != nalUnits[32].end(); nalIt++){ + vpsUnit vps(*nalIt); + vps.updateHVCC(hvccBox); + } + for (nalIt = nalUnits[33].begin(); nalIt != nalUnits[33].end(); nalIt++){ + spsUnit sps(*nalIt); + sps.updateHVCC(hvccBox); + } + //NOTE: We dont parse the ppsUnit, as the only information it contains is parallelism mode, which is set to 0 for 'unknown' + std::deque hvccArrays; + hvccArrays.resize(3); + hvccArrays[0].arrayCompleteness = 0; + hvccArrays[0].nalUnitType = 32; + for (nalIt = nalUnits[32].begin(); nalIt != nalUnits[32].end(); nalIt++){ + hvccArrays[0].nalUnits.push_back(*nalIt); + } + hvccArrays[1].arrayCompleteness = 0; + hvccArrays[1].nalUnitType = 33; + for (nalIt = nalUnits[33].begin(); nalIt != nalUnits[33].end(); nalIt++){ + hvccArrays[1].nalUnits.push_back(*nalIt); + } + hvccArrays[2].arrayCompleteness = 0; + hvccArrays[2].nalUnitType = 34; + for (nalIt = nalUnits[34].begin(); nalIt != nalUnits[34].end(); nalIt++){ + hvccArrays[2].nalUnits.push_back(*nalIt); + } + hvccBox.setArrays(hvccArrays); + hvccBox.setLengthSizeMinus1(3); + return std::string(hvccBox.payload(), hvccBox.payloadSize()); + } + + void updateProfileTierLevel(Utils::bitstream & bs, MP4::HVCC & hvccBox, unsigned int maxSubLayersMinus1){ + hvccBox.setGeneralProfileSpace(bs.get(2)); + + unsigned int tierFlag = bs.get(1); + hvccBox.setGeneralProfileIdc(std::max((unsigned long long)hvccBox.getGeneralProfileIdc(), bs.get(5))); + hvccBox.setGeneralProfileCompatibilityFlags(hvccBox.getGeneralProfileCompatibilityFlags() & bs.get(32)); + hvccBox.setGeneralConstraintIndicatorFlags(hvccBox.getGeneralConstraintIndicatorFlags() & bs.get(48)); + unsigned int levelIdc = bs.get(8); + + if (tierFlag && !hvccBox.getGeneralTierFlag()) { + hvccBox.setGeneralLevelIdc(levelIdc); + }else { + hvccBox.setGeneralLevelIdc(std::max((unsigned int)hvccBox.getGeneralLevelIdc(),levelIdc)); + } + hvccBox.setGeneralTierFlag(tierFlag || hvccBox.getGeneralTierFlag()); + + + //Remainder is for synchronsation of the parser + std::deque profilePresent; + std::deque levelPresent; + + for (int i = 0; i < maxSubLayersMinus1; i++){ + profilePresent.push_back(bs.get(1)); + levelPresent.push_back(bs.get(1)); + } + + if (maxSubLayersMinus1){ + for (int i = maxSubLayersMinus1; i < 8; i++){ + bs.skip(2); + } + } + + for (int i = 0; i < maxSubLayersMinus1; i++){ + if (profilePresent[i]){ + bs.skip(32); + bs.skip(32); + bs.skip(24); + } + if (levelPresent[i]){ + bs.skip(8); + } + } + } + + vpsUnit::vpsUnit(const std::string & _data){ + data = nalu::removeEmulationPrevention(_data); + } + + void vpsUnit::updateHVCC(MP4::HVCC & hvccBox) { + Utils::bitstream bs; + bs.append(data); + bs.skip(16);//Nal Header + bs.skip(12); + + unsigned int maxSubLayers = bs.get(3) + 1; + + hvccBox.setNumberOfTemporalLayers(std::max((unsigned int)hvccBox.getNumberOfTemporalLayers(), maxSubLayers)); + + bs.skip(17); + + updateProfileTierLevel(bs, hvccBox, maxSubLayers - 1); + } + + spsUnit::spsUnit(const std::string & _data){ + data = nalu::removeEmulationPrevention(_data); + } + + void spsUnit::updateHVCC(MP4::HVCC & hvccBox) { + Utils::bitstream bs; + bs.append(data); + bs.skip(16);//Nal Header + bs.skip(4); + + unsigned int maxSubLayers = bs.get(3) + 1; + + hvccBox.setNumberOfTemporalLayers(std::max((unsigned int)hvccBox.getNumberOfTemporalLayers(), maxSubLayers)); + hvccBox.setTemporalIdNested(bs.get(1)); + updateProfileTierLevel(bs, hvccBox, maxSubLayers - 1); + + bs.getUExpGolomb(); + + hvccBox.setChromaFormat(bs.getUExpGolomb()); + + if (hvccBox.getChromaFormat() == 3){ + bs.skip(1); + } + + bs.getUExpGolomb(); + bs.getUExpGolomb(); + + if (bs.get(1)){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + + hvccBox.setBitDepthLumaMinus8(bs.getUExpGolomb()); + hvccBox.setBitDepthChromaMinus8(bs.getUExpGolomb()); + + int log2MaxPicOrderCntLsb = bs.getUExpGolomb() + 4; + + for (int i = bs.get(1) ? 0 : (maxSubLayers - 1); i < maxSubLayers; i++){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + + if (bs.get(1) && bs.get(1)){ + for (int i = 0; i < 4; i++){ + for (int j = 0; j < (i == 3 ? 2 : 6); j++){ + if (!bs.get(1)){ + bs.getUExpGolomb(); + }else{ + int numCoeffs = std::min(64, 1 << (4 + (i << 1))); + if (i > 1){ + bs.getExpGolomb(); + } + + for (int k = 0; k < numCoeffs; k++){ + bs.getExpGolomb(); + } + } + } + } + } + + bs.skip(2); + + if (bs.get(1)){ + bs.skip(8); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.skip(1); + } + + unsigned long long shortTermRefPicSets = bs.getUExpGolomb(); + for (int i = 0; i < shortTermRefPicSets; i++){ + //parse rps, return if ret < 0 + } + + if (bs.get(1)){ + if (log2MaxPicOrderCntLsb > 16){ + log2MaxPicOrderCntLsb = 16; + } + int numLongTermRefPicsSps = bs.getUExpGolomb(); + for (int i = 0; i < numLongTermRefPicsSps; i++){ + bs.skip(log2MaxPicOrderCntLsb + 1); + } + } + + bs.skip(2); + + if (bs.get(1)){ + //parse vui + if (bs.get(1) && bs.get(8) == 255){ bs.skip(32); } + + if (bs.get(1)){ bs.skip(1); } + + if (bs.get(1)){ + bs.skip(4); + if (bs.get(1)){ bs.skip(24); } + } + + if (bs.get(1)){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + + bs.skip(3); + + if (bs.get(1)){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + + if (bs.get(1)){ + bs.skip(32); + bs.skip(32); + if (bs.get(1)){ + bs.getUExpGolomb(); + } + if (bs.get(1)){ + int nalHrd = bs.get(1); + int vclHrd = bs.get(1); + int subPicPresent = 0; + if (nalHrd || vclHrd){ + subPicPresent = bs.get(1); + if (subPicPresent){ + bs.skip(19); + } + bs.skip(8); + if (subPicPresent){ + bs.skip(4); + } + bs.skip(15); + } + + // + for (int i = 0; i < maxSubLayers; i++){ + int cpbCnt = 1; + int lowDelay = 0; + int fixedRateCvs = 0; + int fixedRateGeneral = bs.get(1); + + if (fixedRateGeneral){ + fixedRateCvs = bs.get(1); + } + + if (fixedRateCvs){ + bs.getUExpGolomb(); + }else{ + lowDelay = bs.get(1); + } + + if (!lowDelay){ + cpbCnt = bs.getUExpGolomb() + 1; + } + + if (nalHrd){ + for (int i = 0; i < cpbCnt; i++){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + if (subPicPresent){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + bs.skip(1); + } + } + + if (vclHrd){ + for (int i = 0; i < cpbCnt; i++){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + if (subPicPresent){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + bs.skip(1); + } + } + + } + } + } + + if (bs.get(1)){ + bs.skip(3); + int spatialSegmentIdc = bs.getUExpGolomb(); + hvccBox.setMinSpatialSegmentationIdc(std::min((int)hvccBox.getMinSpatialSegmentationIdc(),spatialSegmentIdc)); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + } + } +} diff --git a/lib/h265.h b/lib/h265.h new file mode 100644 index 00000000..bec6d8d5 --- /dev/null +++ b/lib/h265.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include + +#include "nal.h" +#include "mp4_generic.h" +#include "bitstream.h" + +namespace h265 { + std::deque analysePackets(const char * data, unsigned long len); + + void updateProfileTierLevel(Utils::bitstream & bs, MP4::HVCC & hvccBox, unsigned long maxSubLayersMinus1); + + class initData { + public: + initData(); + void addUnit(char * data); + bool haveRequired(); + std::string generateHVCC(); + protected: + std::map > nalUnits; + }; + + class vpsUnit { + public: + vpsUnit(const std::string & _data); + void updateHVCC(MP4::HVCC & hvccBox); + private: + std::string data; + }; + + class spsUnit { + public: + spsUnit(const std::string & _data); + void updateHVCC(MP4::HVCC & hvccBox); + private: + std::string data; + }; + + //NOTE: no ppsUnit, as the only information it contains is parallelism mode, which can be set to 0 for 'unknown' +} diff --git a/lib/mp4_generic.cpp b/lib/mp4_generic.cpp index eb133637..1ccceb5e 100644 --- a/lib/mp4_generic.cpp +++ b/lib/mp4_generic.cpp @@ -730,23 +730,37 @@ namespace MP4 { return getInt8(18) & 0x07; } - void setAverageFramerate(short newFramerate); + void HVCC::setAverageFramerate(short newFramerate) { + setInt16(newFramerate, 19); + } short HVCC::getAverageFramerate(){ return getInt16(19); } - void setConstantFramerate(char newFramerate); + + void HVCC::setConstantFramerate(char newFramerate) { + setInt8((getInt8(21) & 0x3F) | ((newFramerate & 0x03) << 6), 21); + } char HVCC::getConstantFramerate(){ return (getInt8(21) >> 6) & 0x03; } - void setNumberOfTemporalLayers(char newNumber); + + void HVCC::setNumberOfTemporalLayers(char newNumber) { + setInt8((getInt8(21) & 0xC7) | ((newNumber & 0x07) << 3), 21); + } char HVCC::getNumberOfTemporalLayers(){ return (getInt8(21) >> 3) & 0x07; } - void setTemporalIdNested(char newNested); + + void HVCC::setTemporalIdNested(char newNested){ + setInt8((getInt8(21) & 0xFB) | ((newNested & 0x01) << 2), 21); + } char HVCC::getTemporalIdNested(){ return (getInt8(21) >> 2) & 0x01; } - void setLengthSizeMinus1(char newLengthSizeMinus1); + + void HVCC::setLengthSizeMinus1(char newLengthSizeMinus1) { + setInt8( (getInt8(21) & 0xFC) | (newLengthSizeMinus1 & 0x03), 21); + } char HVCC::getLengthSizeMinus1(){ return getInt8(21) & 0x03; } @@ -776,6 +790,25 @@ namespace MP4 { return r; } + void HVCC::setArrays(std::deque & arrays){ + setInt8(arrays.size(), 22); + int offset = 23; + for (int i = 0; i < arrays.size(); i++){ + HVCCArrayEntry & ref = arrays[i]; + setInt8(((ref.arrayCompleteness & 0x01) << 7) | (arrays[i].nalUnitType & 0x3F), offset++); + setInt16(ref.nalUnits.size(), offset); + offset += 2; + for (int j = 0; j < ref.nalUnits.size(); j++){ + std::string & nalUnit = ref.nalUnits[j]; + setInt16(nalUnit.size(), offset); + offset += 2; + for (std::string::iterator it = nalUnit.begin(); it != nalUnit.end(); it++){ + setInt8(*it, offset++); + } + } + } + } + std::string HVCC::toPrettyString(uint32_t indent) { std::stringstream r; r << std::string(indent, ' ') << "[hvcC] H.265 Init Data (" << boxedSize() << ")" << std::endl; diff --git a/lib/mp4_generic.h b/lib/mp4_generic.h index eafa82ea..093e62e4 100644 --- a/lib/mp4_generic.h +++ b/lib/mp4_generic.h @@ -167,6 +167,7 @@ namespace MP4 { void setLengthSizeMinus1(char newLengthSizeMinus1); char getLengthSizeMinus1(); ///\todo Add setter for the array entries + void setArrays(std::deque & arrays); std::deque getArrays(); std::string asAnnexB(); void setPayload(std::string newPayload); diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 674f7182..3bcc8f18 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -1035,7 +1035,7 @@ namespace TS { if (myMeta.tracks[*it].codec == "H264"){ PMT.setStreamType(0x1B,id); }else if (myMeta.tracks[*it].codec == "HEVC"){ - PMT.setStreamType(0x06,id); + PMT.setStreamType(0x24,id); }else if (myMeta.tracks[*it].codec == "AAC"){ PMT.setStreamType(0x0F,id); }else if (myMeta.tracks[*it].codec == "MP3"){ diff --git a/lib/ts_stream.cpp b/lib/ts_stream.cpp index 650d2f18..fede4f3d 100644 --- a/lib/ts_stream.cpp +++ b/lib/ts_stream.cpp @@ -1,6 +1,7 @@ #include "ts_stream.h" #include "defines.h" #include "h264.h" +#include "h265.h" #include "nal.h" #include "mp4_generic.h" @@ -22,22 +23,37 @@ namespace TS { int tid = newPack.getPID(); if (tid == 0){ associationTable = newPack; + pmtTracks.clear(); + int pmtCount = associationTable.getProgramCount(); + for (int i = 0; i < pmtCount; i++){ + pmtTracks.insert(associationTable.getProgramPID(i)); + } return; } //If we are here, the packet is not a PAT. //First check if it is listed in the PAT as a PMT track. int pmtCount = associationTable.getProgramCount(); - for (int i = 0; i < pmtCount; i++){ - if (tid == associationTable.getProgramPID(i)){ - mappingTable[tid] = newPack; - ProgramMappingEntry entry = mappingTable[tid].getEntry(0); - while (entry){ - unsigned long pid = entry.getElementaryPid(); - pidToCodec[pid] = entry.getStreamType(); - entry.advance(); + if (pmtTracks.count(tid)){ + mappingTable[tid] = newPack; + ProgramMappingEntry entry = mappingTable[tid].getEntry(0); + while (entry){ + unsigned long pid = entry.getElementaryPid(); + switch(entry.getStreamType()){ + case H264: + case AAC: + case HEVC: + case H265: + case AC3: + if (!pidToCodec.count(pid)){ + pidToCodec[pid] = entry.getStreamType(); + } + break; + default: + break; } - return; + entry.advance(); } + return; } //If it is not a PMT, check the list of all PMTs to see if this is a new PES track. bool inPMT = false; @@ -70,6 +86,9 @@ namespace TS { if (!pidToCodec.size()){ return false; } + if (outPackets.size() != pidToCodec.size()){ + return false; + } for (std::map::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){ if (!outPackets.count(it->first) || !outPackets.at(it->first).size()){ return false; @@ -85,11 +104,6 @@ namespace TS { if (outPackets.count(tid) && outPackets.at(tid).size()){ return true; } - for (int i = 1; i < pesStreams.find(tid)->second.size(); i++) { - if (pesStreams.find(tid)->second.at(i).getUnitStart()) { - return true; - } - } return false; } @@ -106,6 +120,7 @@ namespace TS { void Stream::parsePES(unsigned long tid){ std::deque & inStream = pesStreams[tid]; + std::deque & inPositions = pesPositions[tid]; if (inStream.size() == 1){ return; } @@ -113,17 +128,20 @@ namespace TS { return; } - unsigned long long bPos = pesPositions[tid].front(); + unsigned long long bPos = inPositions.front(); //Create a buffer for the current PES, and remove it from the pesStreams buffer. int paySize = payloadSize[tid]; char * payload = (char*)malloc(paySize); int offset = 0; - while (inStream.size() != 1){ - memcpy(payload + offset, inStream.front().getPayload(), inStream.front().getPayloadLength()); - offset += inStream.front().getPayloadLength(); - inStream.pop_front(); - pesPositions[tid].pop_front(); + int packNum = inStream.size() - 1; + std::deque::iterator curPack = inStream.begin(); + for (int i = 0; i < packNum; i++){ + memcpy(payload + offset, curPack->getPayload(), curPack->getPayloadLength()); + offset += curPack->getPayloadLength(); + curPack++; } + inStream.erase(inStream.begin(), curPack); + inPositions.erase(inPositions.begin(), inPositions.begin() + packNum); //Parse the PES header offset = 0; @@ -133,7 +151,7 @@ namespace TS { //Check for large enough buffer if ((paySize - offset) < 9 || (paySize - offset) < 9 + pesHeader[8]){ - INFO_MSG("Not enough data on track %lu, discarding remainder of data", tid); + INFO_MSG("Not enough data on track %lu (%d / %d), discarding remainder of data", tid, paySize - offset, 9 + pesHeader[8]); break; } @@ -150,11 +168,12 @@ namespace TS { if (!realPayloadSize){ realPayloadSize = paySize; } - if (pidToCodec[tid] == AAC){ + if (pidToCodec[tid] == AAC || pidToCodec[tid] == MP3 || pidToCodec[tid] == AC3){ realPayloadSize -= (3 + pesHeader[8]); }else{ realPayloadSize -= (9 + pesHeader[8]); } + //Read the metadata for this PES Packet ///\todo Determine keyframe-ness @@ -195,28 +214,61 @@ namespace TS { offsetInPes += adtsPack.getHeaderSize() + adtsPack.getPayloadSize(); } } - if (pidToCodec[tid] == H264){ + if (pidToCodec[tid] == AC3){ + outPackets[tid].push_back(DTSC::Packet()); + outPackets[tid].back().genericFill(timeStamp, timeOffset, tid, pesPayload, realPayloadSize, bPos, 0); + } + if (pidToCodec[tid] == H264 || pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ //Convert from annex b - char * parsedData = NULL; + char * parsedData = (char*)malloc(realPayloadSize * 2); bool isKeyFrame = false; - unsigned long parsedSize = h264::fromAnnexB(pesPayload, realPayloadSize, parsedData); - std::deque nalInfo = h264::analyseH264Packet(parsedData, parsedSize); + unsigned long parsedSize = nalu::fromAnnexB(pesPayload, realPayloadSize, parsedData); + std::deque nalInfo; + if (pidToCodec[tid] == H264) { + nalInfo = h264::analysePackets(parsedData, parsedSize); + } + if (pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ + nalInfo = h265::analysePackets(parsedData, parsedSize); + } int dataOffset = 0; - for (std::deque::iterator it = nalInfo.begin(); it != nalInfo.end(); it++){ - switch (it->nalType){ - case 0x05: { - isKeyFrame = true; - break; + for (std::deque::iterator it = nalInfo.begin(); it != nalInfo.end(); it++){ + if (pidToCodec[tid] == H264){ + switch (it->nalType){ + case 0x05: { + isKeyFrame = true; + break; + } + case 0x07: { + spsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize); + break; + } + case 0x08: { + ppsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize); + break; + } + default: break; } - case 0x07: { - spsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize); - break; + } + if (pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){ + switch (it->nalType){ + case 2: case 3: //TSA Picture + case 4: case 5: //STSA Picture + case 6: case 7: //RADL Picture + case 8: case 9: //RASL Picture + case 16: case 17: case 18: //BLA Picture + case 19: case 20: //IDR Picture + case 21: { //CRA Picture + isKeyFrame = true; + break; + } + case 32: + case 33: + case 34: { + hevcInfo[tid].addUnit(parsedData + dataOffset); + break; + } + default: break; } - case 0x08: { - ppsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize); - break; - } - default: break; } dataOffset += 4 + it->nalSize; } @@ -230,7 +282,7 @@ namespace TS { }else{ realPayloadSize += (9 + pesHeader[8]); } - offset += realPayloadSize; + offset += realPayloadSize + 6; } free(payload); payloadSize[tid] = inStream.front().getPayloadLength(); @@ -243,21 +295,11 @@ namespace TS { return; } - //Handle the situation where we have DTSC Packets buffered - if (outPackets[tid].size()){ - pack = outPackets[tid].front(); - outPackets[tid].pop_front(); - if (!outPackets[tid].size()){ - payloadSize[tid] = 0; - for (std::deque::iterator it = pesStreams[tid].begin(); it != pesStreams[tid].end(); it++){ - //Break this loop on the second TS Packet with the UnitStart flag set, not on the first. - if (it->getUnitStart() && it != pesStreams[tid].begin()){ - break; - } - payloadSize[tid] += it->getPayloadLength(); - } - } - return; + pack = outPackets[tid].front(); + outPackets[tid].pop_front(); + + if (!outPackets[tid].size()){ + outPackets.erase(tid); } } @@ -276,8 +318,8 @@ namespace TS { packTime = it->second.front().getTime(); } } - pack = outPackets[packTrack].front(); - outPackets[packTrack].pop_front(); + + getPacket(packTrack, pack); } void Stream::initializeMetadata(DTSC::Meta & meta) { @@ -305,7 +347,24 @@ namespace TS { avccBox.setPPSNumber(1); avccBox.setPPS(ppsInfo[it->first]); meta.tracks[it->first].init = std::string(avccBox.payload(), avccBox.payloadSize()); - INFO_MSG("Initialized metadata for track %lu, with an SPS of %lu bytes, and a PPS of %lu bytes", it->first, spsInfo[it->first].size(), ppsInfo[it->first].size()); + } + 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"; + 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 == 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"; diff --git a/lib/ts_stream.h b/lib/ts_stream.h index 3c6c6341..c68d709b 100644 --- a/lib/ts_stream.h +++ b/lib/ts_stream.h @@ -2,12 +2,16 @@ #include "adts.h" #include #include +#include "h265.h" namespace TS { enum codecType { H264 = 0x1B, AAC = 0x0F, - AC3 = 0x81 + AC3 = 0x81, + MP3 = 0x03, + HEVC = 0x06, + H265 = 0x24 }; class Stream{ @@ -31,6 +35,10 @@ namespace TS { std::map adtsInfo; std::map spsInfo; std::map ppsInfo; + std::map hevcInfo; + + + std::set pmtTracks; void parsePES(unsigned long tid); }; diff --git a/src/analysers/tsstream_analyser.cpp b/src/analysers/tsstream_analyser.cpp index 6a3b5fd0..a0f7da0a 100755 --- a/src/analysers/tsstream_analyser.cpp +++ b/src/analysers/tsstream_analyser.cpp @@ -32,10 +32,11 @@ namespace Analysers { if(std::cin.gcount() != 188){break;} bytes += 188; if(packet.FromPointer(packetPtr)){ + //std::cout << packet.toPrettyString(); tsStream.parse(packet, bytes); - if (tsStream.hasPacketOnEachTrack()){ + if (tsStream.hasPacket(packet.getPID())){ DTSC::Packet dtscPack; - tsStream.getEarliestPacket(dtscPack); + tsStream.getPacket(packet.getPID(), dtscPack); std::cout << dtscPack.toJSON().toPrettyString(); } } diff --git a/src/input/input.cpp b/src/input/input.cpp index a0aece7a..b4b3fa2e 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -204,7 +204,14 @@ namespace Mist { char userPageName[NAME_BUFFER_SIZE]; snprintf(userPageName, NAME_BUFFER_SIZE, SHM_USERS, streamName.c_str()); #ifdef INPUT_LIVE - Util::startInput(streamName); + unsigned int giveUpCounter = 0; + while (!Util::startInput(streamName) && config->is_active && ++giveUpCounter < 20) { + Util::sleep(500); + } + if (giveUpCounter >= 20){ + FAIL_MSG("Could not start buffer for stream '%s', aborting stream input!", streamName.c_str()); + config->is_active = false; + } userClient = IPC::sharedClient(userPageName, 30, true); getNext(); while (thisPacket || config->is_active) { @@ -253,7 +260,6 @@ namespace Mist { long long int activityCounter = Util::bootSecs(); while ((Util::bootSecs() - activityCounter) < 10 && config->is_active) { //10 second timeout - Util::wait(1000); userPage.parseEach(callbackWrapper); removeUnused(); if (userPage.amount) { @@ -273,6 +279,7 @@ namespace Mist { } } /*LTS-END*/ + Util::sleep(1000); } #endif finish(); diff --git a/src/input/input_ts.cpp b/src/input/input_ts.cpp index 03b94cdc..08ed2d29 100755 --- a/src/input/input_ts.cpp +++ b/src/input/input_ts.cpp @@ -39,6 +39,7 @@ namespace Mist { capa["source_match"] = "/*.ts"; capa["priority"] = 9ll; capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][0u].append("HEVC"); capa["codecs"][0u][1u].append("AAC"); capa["codecs"][0u][1u].append("AC3"); @@ -51,6 +52,13 @@ namespace Mist { JSON::fromString("{\"arg\":\"integer\",\"value\":9876,\"short\":\"p\",\"long\":\"port\",\"help\":\"The udp port on which to listen for incoming UDP Packets.\"}")); pushing = false; + inFile = NULL; + } + + inputTS::~inputTS() { + if (inFile){ + fclose(inFile); + } } ///Setup of TS Input @@ -113,7 +121,7 @@ namespace Mist { bool first = true; long long int lastBpos = 0; - while (packet.FromFile(inFile)){ + while (packet.FromFile(inFile) && !feof(inFile)){ tsStream.parse(packet, lastBpos); lastBpos = ftell(inFile); while(tsStream.hasPacketOnEachTrack()){ @@ -128,10 +136,10 @@ namespace Mist { } + fseek(inFile, 0, SEEK_SET); std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str()); oFile << myMeta.toJSON().toNetPacked(); oFile.close(); - exit(1); return true; } @@ -143,16 +151,15 @@ namespace Mist { void inputTS::getNext(bool smart){ thisPacket.null(); bool hasPacket = (selectedTracks.size() == 1 ? tsStream.hasPacket(*selectedTracks.begin()) : tsStream.hasPacketOnEachTrack()); - - if (!hasPacket && (pushing || !feof(inFile))){ - TS::Packet tsBuf; + while (!hasPacket && (pushing || !feof(inFile)) && config->is_active){ if (!pushing) { unsigned int bPos = ftell(inFile); tsBuf.FromFile(inFile); - tsStream.parse(tsBuf, bPos); + if (selectedTracks.count(tsBuf.getPID())){ + tsStream.parse(tsBuf, bPos); + } }else{ while (udpCon.Receive()){ - userClient.keepAlive(); udpDataBuffer.append(udpCon.data, udpCon.data_len); while (udpDataBuffer.size() > 188 && (udpDataBuffer[0] != 0x47 || udpDataBuffer[188] != 0x47)){ size_t syncPos = udpDataBuffer.find("\107", 1); @@ -164,10 +171,23 @@ namespace Mist { udpDataBuffer.erase(0,188); } } + if (userClient.getData()){ + userClient.keepAlive(); + } + Util::sleep(500); + } + if (userClient.getData()){ + userClient.keepAlive(); } hasPacket = (selectedTracks.size() == 1 ? tsStream.hasPacket(*selectedTracks.begin()) : tsStream.hasPacketOnEachTrack()); } if (!hasPacket){ + if(inFile && !feof(inFile)){ + getNext(); + } + if (pushing){ + sleep(500); + } return; } if (selectedTracks.size() == 1){ @@ -180,10 +200,35 @@ namespace Mist { getNext(); } } + + void inputTS::readPMT(){ + //save current file position + int bpos = ftell(inFile); + if (fseek(inFile, 0, SEEK_SET)){ + FAIL_MSG("Seek to 0 failed"); + return; + } + + TS::Packet tsBuffer; + while (!tsStream.hasPacketOnEachTrack() && tsBuffer.FromFile(inFile)){ + tsStream.parse(tsBuffer, 0); + } + + //Clear leaves the PMT in place + tsStream.clear(); + + + //Restore original file position + if (fseek(inFile, bpos, SEEK_SET)){ + return; + } + + } ///Seeks to a specific time void inputTS::seek(int seekTime){ tsStream.clear(); + readPMT(); unsigned long seekPos = 0xFFFFFFFFull; for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ unsigned long thisBPos = 0; diff --git a/src/input/input_ts.h b/src/input/input_ts.h index 293b2478..0a347657 100755 --- a/src/input/input_ts.h +++ b/src/input/input_ts.h @@ -12,6 +12,7 @@ namespace Mist { class inputTS : public Input { public: inputTS(Util::Config * cfg); + ~inputTS(); protected: //Private Functions bool setup(); @@ -19,6 +20,7 @@ namespace Mist { void getNext(bool smart = true); void seek(int seekTime); void trackSelect(std::string trackSpec); + void readPMT(); FILE * inFile;/// nalSizes = h264::parseNalSizes(thisPacket); + std::deque nalSizes = nalu::parseNalSizes(thisPacket); for(std::deque::iterator it = nalSizes.begin(); it != nalSizes.end(); it++){ int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16 MP4::UUID_SampleEncryption_Sample_Entry newEntry;