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/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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
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;
|
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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"){
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue