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/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

View file

@ -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);

View file

@ -129,7 +129,7 @@ namespace Encryption {
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++) {
int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16
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;
}
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<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::stringstream r;
r << std::string(indent, ' ') << "[hvcC] H.265 Init Data (" << boxedSize() << ")" << std::endl;

View file

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

View file

@ -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"){

View file

@ -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<unsigned long, unsigned long>::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<Packet> & inStream = pesStreams[tid];
std::deque<unsigned long long> & 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<Packet>::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,12 +168,13 @@ 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
unsigned int timeStamp = 0;
@ -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<h264::nalData> nalInfo = h264::analyseH264Packet(parsedData, parsedSize);
unsigned long parsedSize = nalu::fromAnnexB(pesPayload, realPayloadSize, parsedData);
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;
for (std::deque<h264::nalData>::iterator it = nalInfo.begin(); it != nalInfo.end(); it++){
switch (it->nalType){
case 0x05: {
isKeyFrame = true;
break;
for (std::deque<nalu::nalData>::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<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;
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";

View file

@ -2,12 +2,16 @@
#include "adts.h"
#include <map>
#include <deque>
#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<unsigned long, aac::adts > adtsInfo;
std::map<unsigned long, std::string > spsInfo;
std::map<unsigned long, std::string > ppsInfo;
std::map<unsigned long, h265::initData > hevcInfo;
std::set<unsigned long> pmtTracks;
void parsePES(unsigned long tid);
};

View file

@ -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();
}
}

View file

@ -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();

View file

@ -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){
@ -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
void inputTS::seek(int seekTime){
tsStream.clear();
readPMT();
unsigned long seekPos = 0xFFFFFFFFull;
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
unsigned long thisBPos = 0;

View file

@ -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;///<The input file with ts data
TS::Stream tsStream;///<Used for parsing the incoming ts stream
@ -26,6 +28,8 @@ namespace Mist {
bool pushing;
Socket::UDPConnection udpCon;
std::string udpDataBuffer;
TS::Packet tsBuf;
};
}

View file

@ -300,7 +300,7 @@ namespace Mist {
MP4::UUID_SampleEncryption_Sample newSample;
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++){
int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16
MP4::UUID_SampleEncryption_Sample_Entry newEntry;