Better working TS input, Pro side. Code by Erik Zandvliet.
This commit is contained in:
parent
8bcda5e57b
commit
35b2dd6bee
15 changed files with 627 additions and 78 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
342
lib/h265.cpp
Normal 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
42
lib/h265.h
Normal 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'
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"){
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue