Better working TS input, Pro side. Code by Erik Zandvliet.

This commit is contained in:
Thulinma 2015-11-05 16:56:50 +01:00
parent 8bcda5e57b
commit 35b2dd6bee
15 changed files with 627 additions and 78 deletions

View file

@ -126,6 +126,7 @@ set(libHeaders
${SOURCE_DIR}/lib/encryption.h ${SOURCE_DIR}/lib/encryption.h
${SOURCE_DIR}/lib/flv_tag.h ${SOURCE_DIR}/lib/flv_tag.h
${SOURCE_DIR}/lib/h264.h ${SOURCE_DIR}/lib/h264.h
${SOURCE_DIR}/lib/h265.h
${SOURCE_DIR}/lib/http_parser.h ${SOURCE_DIR}/lib/http_parser.h
${SOURCE_DIR}/lib/json.h ${SOURCE_DIR}/lib/json.h
${SOURCE_DIR}/lib/mp4_adobe.h ${SOURCE_DIR}/lib/mp4_adobe.h
@ -169,6 +170,7 @@ set(libSources
${SOURCE_DIR}/lib/encryption.cpp ${SOURCE_DIR}/lib/encryption.cpp
${SOURCE_DIR}/lib/flv_tag.cpp ${SOURCE_DIR}/lib/flv_tag.cpp
${SOURCE_DIR}/lib/h264.cpp ${SOURCE_DIR}/lib/h264.cpp
${SOURCE_DIR}/lib/h265.cpp
${SOURCE_DIR}/lib/http_parser.cpp ${SOURCE_DIR}/lib/http_parser.cpp
${SOURCE_DIR}/lib/json.cpp ${SOURCE_DIR}/lib/json.cpp
${SOURCE_DIR}/lib/mp4_adobe.cpp ${SOURCE_DIR}/lib/mp4_adobe.cpp

View file

@ -19,10 +19,15 @@ namespace aac {
} }
adts::adts(const adts & rhs){ adts::adts(const adts & rhs){
data = NULL;
len = 0;
*this = rhs; *this = rhs;
} }
adts& adts::operator = (const adts & rhs){ adts& adts::operator = (const adts & rhs){
if (data){
free(data);
}
len = rhs.len; len = rhs.len;
data = (char*)malloc(len); data = (char*)malloc(len);
memcpy(data, rhs.data, len); memcpy(data, rhs.data, len);

View file

@ -129,7 +129,7 @@ namespace Encryption {
int pos = 0; int pos = 0;
std::deque<int> nalSizes = h264::parseNalSizes(thisPack); std::deque<int> nalSizes = nalu::parseNalSizes(thisPack);
for (std::deque<int>::iterator it = nalSizes.begin(); it != nalSizes.end(); it++) { for (std::deque<int>::iterator it = nalSizes.begin(); it != nalSizes.end(); it++) {
int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16 int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16
int clear = *it - encrypted; int clear = *it - encrypted;

342
lib/h265.cpp Normal file
View file

@ -0,0 +1,342 @@
#include "h265.h"
#include "bitfields.h"
#include "defines.h"
namespace h265 {
std::deque<nalu::nalData> analysePackets(const char * data, unsigned long len){
std::deque<nalu::nalData> 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<std::string>::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<MP4::HVCCArrayEntry> 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<bool> profilePresent;
std::deque<bool> 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();
}
}
}
}

42
lib/h265.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include <deque>
#include <map>
#include <set>
#include "nal.h"
#include "mp4_generic.h"
#include "bitstream.h"
namespace h265 {
std::deque<nalu::nalData> 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<unsigned int, std::set<std::string> > 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'
}

View file

@ -730,23 +730,37 @@ namespace MP4 {
return getInt8(18) & 0x07; return getInt8(18) & 0x07;
} }
void setAverageFramerate(short newFramerate); void HVCC::setAverageFramerate(short newFramerate) {
setInt16(newFramerate, 19);
}
short HVCC::getAverageFramerate(){ short HVCC::getAverageFramerate(){
return getInt16(19); return getInt16(19);
} }
void setConstantFramerate(char newFramerate);
void HVCC::setConstantFramerate(char newFramerate) {
setInt8((getInt8(21) & 0x3F) | ((newFramerate & 0x03) << 6), 21);
}
char HVCC::getConstantFramerate(){ char HVCC::getConstantFramerate(){
return (getInt8(21) >> 6) & 0x03; 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(){ char HVCC::getNumberOfTemporalLayers(){
return (getInt8(21) >> 3) & 0x07; 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(){ char HVCC::getTemporalIdNested(){
return (getInt8(21) >> 2) & 0x01; return (getInt8(21) >> 2) & 0x01;
} }
void setLengthSizeMinus1(char newLengthSizeMinus1);
void HVCC::setLengthSizeMinus1(char newLengthSizeMinus1) {
setInt8( (getInt8(21) & 0xFC) | (newLengthSizeMinus1 & 0x03), 21);
}
char HVCC::getLengthSizeMinus1(){ char HVCC::getLengthSizeMinus1(){
return getInt8(21) & 0x03; return getInt8(21) & 0x03;
} }
@ -776,6 +790,25 @@ namespace MP4 {
return r; return r;
} }
void HVCC::setArrays(std::deque<HVCCArrayEntry> & 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::string HVCC::toPrettyString(uint32_t indent) {
std::stringstream r; std::stringstream r;
r << std::string(indent, ' ') << "[hvcC] H.265 Init Data (" << boxedSize() << ")" << std::endl; r << std::string(indent, ' ') << "[hvcC] H.265 Init Data (" << boxedSize() << ")" << std::endl;

View file

@ -167,6 +167,7 @@ namespace MP4 {
void setLengthSizeMinus1(char newLengthSizeMinus1); void setLengthSizeMinus1(char newLengthSizeMinus1);
char getLengthSizeMinus1(); char getLengthSizeMinus1();
///\todo Add setter for the array entries ///\todo Add setter for the array entries
void setArrays(std::deque<HVCCArrayEntry> & arrays);
std::deque<HVCCArrayEntry> getArrays(); std::deque<HVCCArrayEntry> getArrays();
std::string asAnnexB(); std::string asAnnexB();
void setPayload(std::string newPayload); void setPayload(std::string newPayload);

View file

@ -1035,7 +1035,7 @@ namespace TS {
if (myMeta.tracks[*it].codec == "H264"){ if (myMeta.tracks[*it].codec == "H264"){
PMT.setStreamType(0x1B,id); PMT.setStreamType(0x1B,id);
}else if (myMeta.tracks[*it].codec == "HEVC"){ }else if (myMeta.tracks[*it].codec == "HEVC"){
PMT.setStreamType(0x06,id); PMT.setStreamType(0x24,id);
}else if (myMeta.tracks[*it].codec == "AAC"){ }else if (myMeta.tracks[*it].codec == "AAC"){
PMT.setStreamType(0x0F,id); PMT.setStreamType(0x0F,id);
}else if (myMeta.tracks[*it].codec == "MP3"){ }else if (myMeta.tracks[*it].codec == "MP3"){

View file

@ -1,6 +1,7 @@
#include "ts_stream.h" #include "ts_stream.h"
#include "defines.h" #include "defines.h"
#include "h264.h" #include "h264.h"
#include "h265.h"
#include "nal.h" #include "nal.h"
#include "mp4_generic.h" #include "mp4_generic.h"
@ -22,22 +23,37 @@ namespace TS {
int tid = newPack.getPID(); int tid = newPack.getPID();
if (tid == 0){ if (tid == 0){
associationTable = newPack; associationTable = newPack;
pmtTracks.clear();
int pmtCount = associationTable.getProgramCount();
for (int i = 0; i < pmtCount; i++){
pmtTracks.insert(associationTable.getProgramPID(i));
}
return; return;
} }
//If we are here, the packet is not a PAT. //If we are here, the packet is not a PAT.
//First check if it is listed in the PAT as a PMT track. //First check if it is listed in the PAT as a PMT track.
int pmtCount = associationTable.getProgramCount(); int pmtCount = associationTable.getProgramCount();
for (int i = 0; i < pmtCount; i++){ if (pmtTracks.count(tid)){
if (tid == associationTable.getProgramPID(i)){ mappingTable[tid] = newPack;
mappingTable[tid] = newPack; ProgramMappingEntry entry = mappingTable[tid].getEntry(0);
ProgramMappingEntry entry = mappingTable[tid].getEntry(0); while (entry){
while (entry){ unsigned long pid = entry.getElementaryPid();
unsigned long pid = entry.getElementaryPid(); switch(entry.getStreamType()){
pidToCodec[pid] = entry.getStreamType(); case H264:
entry.advance(); 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. //If it is not a PMT, check the list of all PMTs to see if this is a new PES track.
bool inPMT = false; bool inPMT = false;
@ -70,6 +86,9 @@ namespace TS {
if (!pidToCodec.size()){ if (!pidToCodec.size()){
return false; return false;
} }
if (outPackets.size() != pidToCodec.size()){
return false;
}
for (std::map<unsigned long, unsigned long>::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){ for (std::map<unsigned long, unsigned long>::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){
if (!outPackets.count(it->first) || !outPackets.at(it->first).size()){ if (!outPackets.count(it->first) || !outPackets.at(it->first).size()){
return false; return false;
@ -85,11 +104,6 @@ namespace TS {
if (outPackets.count(tid) && outPackets.at(tid).size()){ if (outPackets.count(tid) && outPackets.at(tid).size()){
return true; 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; return false;
} }
@ -106,6 +120,7 @@ namespace TS {
void Stream::parsePES(unsigned long tid){ void Stream::parsePES(unsigned long tid){
std::deque<Packet> & inStream = pesStreams[tid]; std::deque<Packet> & inStream = pesStreams[tid];
std::deque<unsigned long long> & inPositions = pesPositions[tid];
if (inStream.size() == 1){ if (inStream.size() == 1){
return; return;
} }
@ -113,17 +128,20 @@ namespace TS {
return; 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. //Create a buffer for the current PES, and remove it from the pesStreams buffer.
int paySize = payloadSize[tid]; int paySize = payloadSize[tid];
char * payload = (char*)malloc(paySize); char * payload = (char*)malloc(paySize);
int offset = 0; int offset = 0;
while (inStream.size() != 1){ int packNum = inStream.size() - 1;
memcpy(payload + offset, inStream.front().getPayload(), inStream.front().getPayloadLength()); std::deque<Packet>::iterator curPack = inStream.begin();
offset += inStream.front().getPayloadLength(); for (int i = 0; i < packNum; i++){
inStream.pop_front(); memcpy(payload + offset, curPack->getPayload(), curPack->getPayloadLength());
pesPositions[tid].pop_front(); offset += curPack->getPayloadLength();
curPack++;
} }
inStream.erase(inStream.begin(), curPack);
inPositions.erase(inPositions.begin(), inPositions.begin() + packNum);
//Parse the PES header //Parse the PES header
offset = 0; offset = 0;
@ -133,7 +151,7 @@ namespace TS {
//Check for large enough buffer //Check for large enough buffer
if ((paySize - offset) < 9 || (paySize - offset) < 9 + pesHeader[8]){ 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; break;
} }
@ -150,12 +168,13 @@ namespace TS {
if (!realPayloadSize){ if (!realPayloadSize){
realPayloadSize = paySize; realPayloadSize = paySize;
} }
if (pidToCodec[tid] == AAC){ if (pidToCodec[tid] == AAC || pidToCodec[tid] == MP3 || pidToCodec[tid] == AC3){
realPayloadSize -= (3 + pesHeader[8]); realPayloadSize -= (3 + pesHeader[8]);
}else{ }else{
realPayloadSize -= (9 + pesHeader[8]); realPayloadSize -= (9 + pesHeader[8]);
} }
//Read the metadata for this PES Packet //Read the metadata for this PES Packet
///\todo Determine keyframe-ness ///\todo Determine keyframe-ness
unsigned int timeStamp = 0; unsigned int timeStamp = 0;
@ -195,28 +214,61 @@ namespace TS {
offsetInPes += adtsPack.getHeaderSize() + adtsPack.getPayloadSize(); 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 //Convert from annex b
char * parsedData = NULL; char * parsedData = (char*)malloc(realPayloadSize * 2);
bool isKeyFrame = false; bool isKeyFrame = false;
unsigned long parsedSize = h264::fromAnnexB(pesPayload, realPayloadSize, parsedData); unsigned long parsedSize = nalu::fromAnnexB(pesPayload, realPayloadSize, parsedData);
std::deque<h264::nalData> nalInfo = h264::analyseH264Packet(parsedData, parsedSize); std::deque<nalu::nalData> 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; int dataOffset = 0;
for (std::deque<h264::nalData>::iterator it = nalInfo.begin(); it != nalInfo.end(); it++){ for (std::deque<nalu::nalData>::iterator it = nalInfo.begin(); it != nalInfo.end(); it++){
switch (it->nalType){ if (pidToCodec[tid] == H264){
case 0x05: { switch (it->nalType){
isKeyFrame = true; case 0x05: {
break; 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); if (pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){
break; 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; dataOffset += 4 + it->nalSize;
} }
@ -230,7 +282,7 @@ namespace TS {
}else{ }else{
realPayloadSize += (9 + pesHeader[8]); realPayloadSize += (9 + pesHeader[8]);
} }
offset += realPayloadSize; offset += realPayloadSize + 6;
} }
free(payload); free(payload);
payloadSize[tid] = inStream.front().getPayloadLength(); payloadSize[tid] = inStream.front().getPayloadLength();
@ -243,21 +295,11 @@ namespace TS {
return; return;
} }
//Handle the situation where we have DTSC Packets buffered pack = outPackets[tid].front();
if (outPackets[tid].size()){ outPackets[tid].pop_front();
pack = outPackets[tid].front();
outPackets[tid].pop_front(); if (!outPackets[tid].size()){
if (!outPackets[tid].size()){ outPackets.erase(tid);
payloadSize[tid] = 0;
for (std::deque<Packet>::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;
} }
} }
@ -276,8 +318,8 @@ namespace TS {
packTime = it->second.front().getTime(); packTime = it->second.front().getTime();
} }
} }
pack = outPackets[packTrack].front();
outPackets[packTrack].pop_front(); getPacket(packTrack, pack);
} }
void Stream::initializeMetadata(DTSC::Meta & meta) { void Stream::initializeMetadata(DTSC::Meta & meta) {
@ -305,7 +347,24 @@ namespace TS {
avccBox.setPPSNumber(1); avccBox.setPPSNumber(1);
avccBox.setPPS(ppsInfo[it->first]); avccBox.setPPS(ppsInfo[it->first]);
meta.tracks[it->first].init = std::string(avccBox.payload(), avccBox.payloadSize()); 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){ if (!meta.tracks.count(it->first) && it->second == AAC){
meta.tracks[it->first].type = "audio"; meta.tracks[it->first].type = "audio";

View file

@ -2,12 +2,16 @@
#include "adts.h" #include "adts.h"
#include <map> #include <map>
#include <deque> #include <deque>
#include "h265.h"
namespace TS { namespace TS {
enum codecType { enum codecType {
H264 = 0x1B, H264 = 0x1B,
AAC = 0x0F, AAC = 0x0F,
AC3 = 0x81 AC3 = 0x81,
MP3 = 0x03,
HEVC = 0x06,
H265 = 0x24
}; };
class Stream{ class Stream{
@ -31,6 +35,10 @@ namespace TS {
std::map<unsigned long, aac::adts > adtsInfo; std::map<unsigned long, aac::adts > adtsInfo;
std::map<unsigned long, std::string > spsInfo; std::map<unsigned long, std::string > spsInfo;
std::map<unsigned long, std::string > ppsInfo; std::map<unsigned long, std::string > ppsInfo;
std::map<unsigned long, h265::initData > hevcInfo;
std::set<unsigned long> pmtTracks;
void parsePES(unsigned long tid); void parsePES(unsigned long tid);
}; };

View file

@ -32,10 +32,11 @@ namespace Analysers {
if(std::cin.gcount() != 188){break;} if(std::cin.gcount() != 188){break;}
bytes += 188; bytes += 188;
if(packet.FromPointer(packetPtr)){ if(packet.FromPointer(packetPtr)){
//std::cout << packet.toPrettyString();
tsStream.parse(packet, bytes); tsStream.parse(packet, bytes);
if (tsStream.hasPacketOnEachTrack()){ if (tsStream.hasPacket(packet.getPID())){
DTSC::Packet dtscPack; DTSC::Packet dtscPack;
tsStream.getEarliestPacket(dtscPack); tsStream.getPacket(packet.getPID(), dtscPack);
std::cout << dtscPack.toJSON().toPrettyString(); std::cout << dtscPack.toJSON().toPrettyString();
} }
} }

View file

@ -204,7 +204,14 @@ namespace Mist {
char userPageName[NAME_BUFFER_SIZE]; char userPageName[NAME_BUFFER_SIZE];
snprintf(userPageName, NAME_BUFFER_SIZE, SHM_USERS, streamName.c_str()); snprintf(userPageName, NAME_BUFFER_SIZE, SHM_USERS, streamName.c_str());
#ifdef INPUT_LIVE #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); userClient = IPC::sharedClient(userPageName, 30, true);
getNext(); getNext();
while (thisPacket || config->is_active) { while (thisPacket || config->is_active) {
@ -253,7 +260,6 @@ namespace Mist {
long long int activityCounter = Util::bootSecs(); long long int activityCounter = Util::bootSecs();
while ((Util::bootSecs() - activityCounter) < 10 && config->is_active) { //10 second timeout while ((Util::bootSecs() - activityCounter) < 10 && config->is_active) { //10 second timeout
Util::wait(1000);
userPage.parseEach(callbackWrapper); userPage.parseEach(callbackWrapper);
removeUnused(); removeUnused();
if (userPage.amount) { if (userPage.amount) {
@ -273,6 +279,7 @@ namespace Mist {
} }
} }
/*LTS-END*/ /*LTS-END*/
Util::sleep(1000);
} }
#endif #endif
finish(); finish();

View file

@ -39,6 +39,7 @@ namespace Mist {
capa["source_match"] = "/*.ts"; capa["source_match"] = "/*.ts";
capa["priority"] = 9ll; capa["priority"] = 9ll;
capa["codecs"][0u][0u].append("H264"); capa["codecs"][0u][0u].append("H264");
capa["codecs"][0u][0u].append("HEVC");
capa["codecs"][0u][1u].append("AAC"); capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("AC3"); 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.\"}")); JSON::fromString("{\"arg\":\"integer\",\"value\":9876,\"short\":\"p\",\"long\":\"port\",\"help\":\"The udp port on which to listen for incoming UDP Packets.\"}"));
pushing = false; pushing = false;
inFile = NULL;
}
inputTS::~inputTS() {
if (inFile){
fclose(inFile);
}
} }
///Setup of TS Input ///Setup of TS Input
@ -113,7 +121,7 @@ namespace Mist {
bool first = true; bool first = true;
long long int lastBpos = 0; long long int lastBpos = 0;
while (packet.FromFile(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()){
@ -128,10 +136,10 @@ namespace Mist {
} }
fseek(inFile, 0, SEEK_SET);
std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str()); std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str());
oFile << myMeta.toJSON().toNetPacked(); oFile << myMeta.toJSON().toNetPacked();
oFile.close(); oFile.close();
exit(1);
return true; return true;
} }
@ -143,16 +151,15 @@ namespace Mist {
void inputTS::getNext(bool smart){ void inputTS::getNext(bool smart){
thisPacket.null(); thisPacket.null();
bool hasPacket = (selectedTracks.size() == 1 ? tsStream.hasPacket(*selectedTracks.begin()) : tsStream.hasPacketOnEachTrack()); bool hasPacket = (selectedTracks.size() == 1 ? tsStream.hasPacket(*selectedTracks.begin()) : tsStream.hasPacketOnEachTrack());
while (!hasPacket && (pushing || !feof(inFile)) && config->is_active){
if (!hasPacket && (pushing || !feof(inFile))){
TS::Packet tsBuf;
if (!pushing) { if (!pushing) {
unsigned int bPos = ftell(inFile); unsigned int bPos = ftell(inFile);
tsBuf.FromFile(inFile); tsBuf.FromFile(inFile);
tsStream.parse(tsBuf, bPos); if (selectedTracks.count(tsBuf.getPID())){
tsStream.parse(tsBuf, bPos);
}
}else{ }else{
while (udpCon.Receive()){ while (udpCon.Receive()){
userClient.keepAlive();
udpDataBuffer.append(udpCon.data, udpCon.data_len); udpDataBuffer.append(udpCon.data, udpCon.data_len);
while (udpDataBuffer.size() > 188 && (udpDataBuffer[0] != 0x47 || udpDataBuffer[188] != 0x47)){ while (udpDataBuffer.size() > 188 && (udpDataBuffer[0] != 0x47 || udpDataBuffer[188] != 0x47)){
size_t syncPos = udpDataBuffer.find("\107", 1); size_t syncPos = udpDataBuffer.find("\107", 1);
@ -164,10 +171,23 @@ namespace Mist {
udpDataBuffer.erase(0,188); 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()); hasPacket = (selectedTracks.size() == 1 ? tsStream.hasPacket(*selectedTracks.begin()) : tsStream.hasPacketOnEachTrack());
} }
if (!hasPacket){ if (!hasPacket){
if(inFile && !feof(inFile)){
getNext();
}
if (pushing){
sleep(500);
}
return; return;
} }
if (selectedTracks.size() == 1){ if (selectedTracks.size() == 1){
@ -181,9 +201,34 @@ namespace Mist {
} }
} }
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 ///Seeks to a specific time
void inputTS::seek(int seekTime){ void inputTS::seek(int seekTime){
tsStream.clear(); tsStream.clear();
readPMT();
unsigned long seekPos = 0xFFFFFFFFull; unsigned long seekPos = 0xFFFFFFFFull;
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
unsigned long thisBPos = 0; unsigned long thisBPos = 0;

View file

@ -12,6 +12,7 @@ namespace Mist {
class inputTS : public Input { class inputTS : public Input {
public: public:
inputTS(Util::Config * cfg); inputTS(Util::Config * cfg);
~inputTS();
protected: protected:
//Private Functions //Private Functions
bool setup(); bool setup();
@ -19,6 +20,7 @@ namespace Mist {
void getNext(bool smart = true); void getNext(bool smart = true);
void seek(int seekTime); void seek(int seekTime);
void trackSelect(std::string trackSpec); void trackSelect(std::string trackSpec);
void readPMT();
FILE * inFile;///<The input file with ts data FILE * inFile;///<The input file with ts data
TS::Stream tsStream;///<Used for parsing the incoming ts stream TS::Stream tsStream;///<Used for parsing the incoming ts stream
@ -26,6 +28,8 @@ namespace Mist {
bool pushing; bool pushing;
Socket::UDPConnection udpCon; Socket::UDPConnection udpCon;
std::string udpDataBuffer; std::string udpDataBuffer;
TS::Packet tsBuf;
}; };
} }

View file

@ -300,7 +300,7 @@ namespace Mist {
MP4::UUID_SampleEncryption_Sample newSample; MP4::UUID_SampleEncryption_Sample newSample;
thisPacket.getString("ivec", newSample.InitializationVector); thisPacket.getString("ivec", newSample.InitializationVector);
std::deque<int> nalSizes = h264::parseNalSizes(thisPacket); std::deque<int> nalSizes = nalu::parseNalSizes(thisPacket);
for(std::deque<int>::iterator it = nalSizes.begin(); it != nalSizes.end(); it++){ for(std::deque<int>::iterator it = nalSizes.begin(); it != nalSizes.end(); it++){
int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16 int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16
MP4::UUID_SampleEncryption_Sample_Entry newEntry; MP4::UUID_SampleEncryption_Sample_Entry newEntry;