Added HLS input
This commit is contained in:
parent
30beda61ce
commit
a9c5334833
6 changed files with 2104 additions and 442 deletions
|
@ -339,6 +339,7 @@ macro(makeInput inputName format)
|
||||||
)
|
)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
makeInput(HLS hls)
|
||||||
makeInput(DTSC dtsc)
|
makeInput(DTSC dtsc)
|
||||||
makeInput(DTSCCrypt dtsccrypt)
|
makeInput(DTSCCrypt dtsccrypt)
|
||||||
makeInput(MP3 mp3)
|
makeInput(MP3 mp3)
|
||||||
|
|
|
@ -8,14 +8,11 @@
|
||||||
|
|
||||||
namespace TS {
|
namespace TS {
|
||||||
|
|
||||||
class ADTSRemainder{
|
void ADTSRemainder::setRemainder(const aac::adts & p, const void * source, const uint32_t avail, const uint32_t bPos) {
|
||||||
public:
|
if (!p.getCompleteSize()) {
|
||||||
char * data;
|
return;
|
||||||
uint32_t max;
|
}
|
||||||
uint32_t now;
|
|
||||||
uint32_t len;
|
|
||||||
uint32_t bpos;
|
|
||||||
void setRemainder(const aac::adts & p, const void * source, const uint32_t avail, const uint32_t bPos){
|
|
||||||
if (max < p.getCompleteSize()) {
|
if (max < p.getCompleteSize()) {
|
||||||
void * newmainder = realloc(data, p.getCompleteSize());
|
void * newmainder = realloc(data, p.getCompleteSize());
|
||||||
if (newmainder) {
|
if (newmainder) {
|
||||||
|
@ -30,25 +27,61 @@ namespace TS {
|
||||||
memcpy(data, source, now);
|
memcpy(data, source, now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ADTSRemainder(){
|
|
||||||
|
void ADTSRemainder::append(const char * p, uint32_t pLen) {
|
||||||
|
if (now + pLen > len) {
|
||||||
|
FAIL_MSG("Data to append does not fit into the remainder");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data + now, p, pLen);
|
||||||
|
now += pLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ADTSRemainder::isComplete() {
|
||||||
|
return (len == now);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTSRemainder::clear() {
|
||||||
|
len = 0;
|
||||||
|
now = 0;
|
||||||
|
bpos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ADTSRemainder::ADTSRemainder() {
|
||||||
data = 0;
|
data = 0;
|
||||||
max = 0;
|
max = 0;
|
||||||
now = 0;
|
now = 0;
|
||||||
len = 0;
|
len = 0;
|
||||||
bpos = 0;
|
bpos = 0;
|
||||||
}
|
}
|
||||||
~ADTSRemainder(){
|
ADTSRemainder::~ADTSRemainder() {
|
||||||
if (data) {
|
if (data) {
|
||||||
free(data);
|
free(data);
|
||||||
data = 0;
|
data = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
uint32_t ADTSRemainder::getLength() {
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ADTSRemainder::getBpos() {
|
||||||
|
return bpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t ADTSRemainder::getTodo() {
|
||||||
|
return len - now;
|
||||||
|
}
|
||||||
|
char * ADTSRemainder::getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
Stream::Stream(bool _threaded) {
|
Stream::Stream(bool _threaded) {
|
||||||
threaded = _threaded;
|
threaded = _threaded;
|
||||||
|
firstPacketFound = false;
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.open("MstTSInputLock", O_CREAT | O_RDWR, ACCESSPERMS, 1);
|
globalSem.open("MstTSInputLock", O_CREAT | O_RDWR, ACCESSPERMS, 1);
|
||||||
if (!globalSem) {
|
if (!globalSem) {
|
||||||
|
@ -72,18 +105,51 @@ namespace TS {
|
||||||
parse(newPacket, bytePos);
|
parse(newPacket, bytePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stream::clear(){
|
void Stream::partialClear() {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.wait();
|
globalSem.wait();
|
||||||
}
|
}
|
||||||
pesStreams.clear();
|
pesStreams.clear();
|
||||||
pesPositions.clear();
|
pesPositions.clear();
|
||||||
outPackets.clear();
|
outPackets.clear();
|
||||||
|
buildPacket.clear();
|
||||||
|
partialBuffer.clear();
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Stream::clear() {
|
||||||
|
partialClear();
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.wait();
|
||||||
|
}
|
||||||
|
pidToCodec.clear();
|
||||||
|
adtsInfo.clear();
|
||||||
|
spsInfo.clear();
|
||||||
|
ppsInfo.clear();
|
||||||
|
hevcInfo.clear();
|
||||||
|
metaInit.clear();
|
||||||
|
descriptors.clear();
|
||||||
|
mappingTable.clear();
|
||||||
|
lastPMT.clear();
|
||||||
|
lastPAT = 0;
|
||||||
|
pmtTracks.clear();
|
||||||
|
remainders.clear();
|
||||||
|
associationTable = ProgramAssociationTable();
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::finish(){
|
||||||
|
if(!pesStreams.size()){return;}
|
||||||
|
|
||||||
|
for(std::map<unsigned long, std::deque<Packet> >::const_iterator i = pesStreams.begin(); i != pesStreams.end();i++){
|
||||||
|
parsePES(i->first,true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Stream::add(char * newPack, unsigned long long bytePos) {
|
void Stream::add(char * newPack, unsigned long long bytePos) {
|
||||||
Packet newPacket;
|
Packet newPacket;
|
||||||
newPacket.FromPointer(newPack);
|
newPacket.FromPointer(newPack);
|
||||||
|
@ -150,7 +216,6 @@ namespace TS {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int pmtCount = associationTable.getProgramCount();
|
int pmtCount = associationTable.getProgramCount();
|
||||||
for (int i = 0; i < pmtCount; i++) {
|
for (int i = 0; i < pmtCount; i++) {
|
||||||
pmtTracks.insert(associationTable.getProgramPID(i));
|
pmtTracks.insert(associationTable.getProgramPID(i));
|
||||||
|
@ -167,6 +232,7 @@ namespace TS {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Ignore conditional access packets. We don't care.
|
//Ignore conditional access packets. We don't care.
|
||||||
if (tid == 1) {
|
if (tid == 1) {
|
||||||
return;
|
return;
|
||||||
|
@ -224,13 +290,20 @@ namespace TS {
|
||||||
|
|
||||||
int packNum = 1;
|
int packNum = 1;
|
||||||
std::deque<Packet> & inStream = pesStreams[tid];
|
std::deque<Packet> & inStream = pesStreams[tid];
|
||||||
|
if (!inStream.rbegin()->getUnitStart()){
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::deque<Packet>::iterator lastPack = inStream.end();
|
||||||
std::deque<Packet>::iterator curPack = inStream.begin();
|
std::deque<Packet>::iterator curPack = inStream.begin();
|
||||||
curPack++;
|
curPack++;
|
||||||
while (curPack != inStream.end() && !curPack->getUnitStart()){
|
while (curPack != lastPack && !curPack->getUnitStart()) {
|
||||||
curPack++;
|
curPack++;
|
||||||
packNum++;
|
packNum++;
|
||||||
}
|
}
|
||||||
if (curPack != inStream.end()){
|
if (curPack != lastPack) {
|
||||||
parsePes = true;
|
parsePes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +318,6 @@ namespace TS {
|
||||||
|
|
||||||
void Stream::parse(Packet & newPack, unsigned long long bytePos) {
|
void Stream::parse(Packet & newPack, unsigned long long bytePos) {
|
||||||
add(newPack, bytePos);
|
add(newPack, bytePos);
|
||||||
|
|
||||||
int tid = newPack.getPID();
|
int tid = newPack.getPID();
|
||||||
parse(tid);
|
parse(tid);
|
||||||
}
|
}
|
||||||
|
@ -254,24 +326,36 @@ namespace TS {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.wait();
|
globalSem.wait();
|
||||||
}
|
}
|
||||||
if (!pidToCodec.size() || pidToCodec.size() != outPackets.size()){
|
if (!pidToCodec.size() ) {
|
||||||
|
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//INFO_MSG("no packet on each track 1, pidtocodec.size: %d, outpacket.size: %d", pidToCodec.size(), outPackets.size());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int missing = 0;
|
||||||
|
uint64_t firstTime = 0xffffffffffffffffull, lastTime = 0;
|
||||||
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 (!hasPacket(it->first)) {
|
if (!hasPacket(it->first)) {
|
||||||
|
missing++;
|
||||||
|
}else{
|
||||||
|
if(outPackets.at(it->first).front().getTime() < firstTime){
|
||||||
|
firstTime = outPackets.at(it->first).front().getTime();
|
||||||
|
}
|
||||||
|
if(outPackets.at(it->first).back().getTime() > lastTime){
|
||||||
|
lastTime = outPackets.at(it->first).back().getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
return (!missing || (missing != pidToCodec.size() && lastTime - firstTime > 2000));
|
||||||
}
|
|
||||||
if (threaded){
|
|
||||||
globalSem.post();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stream::hasPacket(unsigned long tid) const {
|
bool Stream::hasPacket(unsigned long tid) const {
|
||||||
|
@ -291,10 +375,15 @@ namespace TS {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
std::deque<Packet>::const_iterator curPack = pesStreams.at(tid).begin();
|
std::deque<Packet>::const_iterator curPack = pesStreams.at(tid).begin();
|
||||||
|
|
||||||
|
if (curPack != pesStreams.at(tid).end()) {
|
||||||
curPack++;
|
curPack++;
|
||||||
|
}
|
||||||
|
|
||||||
while (curPack != pesStreams.at(tid).end() && !curPack->getUnitStart()) {
|
while (curPack != pesStreams.at(tid).end() && !curPack->getUnitStart()) {
|
||||||
curPack++;
|
curPack++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curPack != pesStreams.at(tid).end()) {
|
if (curPack != pesStreams.at(tid).end()) {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
|
@ -307,6 +396,55 @@ namespace TS {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Stream::hasPacket() const {
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.wait();
|
||||||
|
}
|
||||||
|
if (!pesStreams.size()) {
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outPackets.size()) {
|
||||||
|
for(std::map<unsigned long, std::deque<DTSC::Packet> >::const_iterator i = outPackets.begin(); i != outPackets.end();i++){
|
||||||
|
if(i->second.size()){
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(std::map<unsigned long, std::deque<Packet> >::const_iterator i = pesStreams.begin(); i != pesStreams.end();i++){
|
||||||
|
std::deque<Packet>::const_iterator curPack = i->second.begin();
|
||||||
|
|
||||||
|
if(curPack != i->second.end()){
|
||||||
|
curPack++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(curPack != i->second.end() && !curPack->getUnitStart()){
|
||||||
|
curPack++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(curPack != i->second.end()){
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long long decodePTS(const char * data) {
|
unsigned long long decodePTS(const char * data) {
|
||||||
unsigned long long time;
|
unsigned long long time;
|
||||||
time = ((data[0] >> 1) & 0x07);
|
time = ((data[0] >> 1) & 0x07);
|
||||||
|
@ -318,14 +456,16 @@ namespace TS {
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stream::parsePES(unsigned long tid){
|
void Stream::parsePES(unsigned long tid, bool finished) {
|
||||||
if (!pidToCodec.count(tid)){return;}//skip unknown codecs
|
if (!pidToCodec.count(tid)) {
|
||||||
|
return; //skip unknown codecs
|
||||||
|
}
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.wait();
|
globalSem.wait();
|
||||||
}
|
}
|
||||||
std::deque<Packet> & inStream = pesStreams[tid];
|
std::deque<Packet> & inStream = pesStreams[tid];
|
||||||
std::deque<unsigned long long> & inPositions = pesPositions[tid];
|
std::deque<unsigned long long> & inPositions = pesPositions[tid];
|
||||||
if (inStream.size() == 1){
|
if (inStream.size() <= 1) {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
|
@ -340,7 +480,7 @@ namespace TS {
|
||||||
curPack++;
|
curPack++;
|
||||||
packNum++;
|
packNum++;
|
||||||
}
|
}
|
||||||
if (curPack == inStream.end()){
|
if (!finished && curPack == inStream.end()) {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
|
@ -348,6 +488,7 @@ namespace TS {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long bPos = inPositions.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 = 0;
|
int paySize = 0;
|
||||||
|
|
||||||
|
@ -362,7 +503,10 @@ namespace TS {
|
||||||
curPack = inStream.begin();
|
curPack = inStream.begin();
|
||||||
int lastCtr = curPack->getContinuityCounter() - 1;
|
int lastCtr = curPack->getContinuityCounter() - 1;
|
||||||
for (int i = 0; i < packNum; i++) {
|
for (int i = 0; i < packNum; i++) {
|
||||||
if (curPack->getContinuityCounter() == lastCtr){curPack++; continue;}
|
if (curPack->getContinuityCounter() == lastCtr) {
|
||||||
|
curPack++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (curPack->getContinuityCounter() - lastCtr != 1 && curPack->getContinuityCounter()) {
|
if (curPack->getContinuityCounter() - lastCtr != 1 && curPack->getContinuityCounter()) {
|
||||||
INFO_MSG("Parsing a pes on track %d, missed %d packets", tid, curPack->getContinuityCounter() - lastCtr - 1);
|
INFO_MSG("Parsing a pes on track %d, missed %d packets", tid, curPack->getContinuityCounter() - lastCtr - 1);
|
||||||
}
|
}
|
||||||
|
@ -401,18 +545,15 @@ namespace TS {
|
||||||
unsigned long long realPayloadSize = (((int)pesHeader[4] << 8) | pesHeader[5]);
|
unsigned long long realPayloadSize = (((int)pesHeader[4] << 8) | pesHeader[5]);
|
||||||
if (!realPayloadSize) {
|
if (!realPayloadSize) {
|
||||||
realPayloadSize = paySize;
|
realPayloadSize = paySize;
|
||||||
}
|
|
||||||
if (pidToCodec[tid] == AAC || pidToCodec[tid] == MP3 || pidToCodec[tid] == AC3){
|
|
||||||
realPayloadSize -= (3 + pesHeader[8]);
|
|
||||||
} else {
|
} else {
|
||||||
realPayloadSize -= (9 + pesHeader[8]);
|
realPayloadSize += 6;
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
unsigned int timeOffset = 0;
|
int64_t timeOffset = 0;
|
||||||
unsigned int pesOffset = 9;
|
unsigned int pesOffset = 9;
|
||||||
if ((pesHeader[7] >> 6) & 0x02) { //Check for PTS presence
|
if ((pesHeader[7] >> 6) & 0x02) { //Check for PTS presence
|
||||||
timeStamp = decodePTS(pesHeader + pesOffset);
|
timeStamp = decodePTS(pesHeader + pesOffset);
|
||||||
|
@ -446,7 +587,24 @@ namespace TS {
|
||||||
realPayloadSize = paySize - offset - pesOffset;
|
realPayloadSize = paySize - offset - pesOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
char * pesPayload = payload + offset + pesOffset;
|
const char * pesPayload = pesHeader + pesOffset;
|
||||||
|
|
||||||
|
if (memmem(pesPayload, realPayloadSize, "DTP2", 4) != 0) {
|
||||||
|
INFO_MSG("dtp found");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parseBitstream(tid, pesPayload, realPayloadSize, timeStamp, timeOffset, bPos);
|
||||||
|
|
||||||
|
//We are done with the realpayload size, reverse calculation so we know the correct offset increase.
|
||||||
|
realPayloadSize += (9 + pesHeader[8]);
|
||||||
|
offset += realPayloadSize;
|
||||||
|
}
|
||||||
|
free(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Stream::parseBitstream(uint32_t tid, const char * pesPayload, uint32_t realPayloadSize, uint64_t timeStamp, int64_t timeOffset, uint64_t bPos) {
|
||||||
|
|
||||||
//Create a new (empty) DTSC Packet at the end of the buffer
|
//Create a new (empty) DTSC Packet at the end of the buffer
|
||||||
if (pidToCodec[tid] == AAC) {
|
if (pidToCodec[tid] == AAC) {
|
||||||
|
@ -456,22 +614,22 @@ namespace TS {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.wait();
|
globalSem.wait();
|
||||||
}
|
}
|
||||||
static std::map<unsigned long, ADTSRemainder> remainders;
|
|
||||||
if (remainders.count(tid) && remainders[tid].len){
|
if (remainders.count(tid) && remainders[tid].getLength()) {
|
||||||
offsetInPes = std::min((unsigned long)(remainders[tid].len - remainders[tid].now), (unsigned long)realPayloadSize);
|
offsetInPes = std::min((unsigned long)(remainders[tid].getTodo()), (unsigned long)realPayloadSize);
|
||||||
memcpy(remainders[tid].data+remainders[tid].now, pesPayload, offsetInPes);
|
remainders[tid].append(pesPayload, offsetInPes);
|
||||||
remainders[tid].now += offsetInPes;
|
|
||||||
if (remainders[tid].now == remainders[tid].len){
|
if (remainders[tid].isComplete()) {
|
||||||
aac::adts adtsPack(remainders[tid].data, remainders[tid].len);
|
aac::adts adtsPack(remainders[tid].getData(), remainders[tid].getLength());
|
||||||
if (adtsPack) {
|
if (adtsPack) {
|
||||||
if (!adtsInfo.count(tid) || !adtsInfo[tid].sameHeader(adtsPack)) {
|
if (!adtsInfo.count(tid) || !adtsInfo[tid].sameHeader(adtsPack)) {
|
||||||
MEDIUM_MSG("Setting new ADTS header: %s", adtsPack.toPrettyString().c_str());
|
MEDIUM_MSG("Setting new ADTS header: %s", adtsPack.toPrettyString().c_str());
|
||||||
adtsInfo[tid] = adtsPack;
|
adtsInfo[tid] = adtsPack;
|
||||||
}
|
}
|
||||||
outPackets[tid].push_back(DTSC::Packet());
|
outPackets[tid].push_back(DTSC::Packet());
|
||||||
outPackets[tid].back().genericFill(timeStamp-((adtsPack.getSampleCount() * 1000) / adtsPack.getFrequency()), timeOffset, tid, adtsPack.getPayload(), adtsPack.getPayloadSize(), remainders[tid].bpos, 0);
|
outPackets[tid].back().genericFill(timeStamp - ((adtsPack.getSampleCount() * 1000) / adtsPack.getFrequency()), timeOffset, tid, adtsPack.getPayload(), adtsPack.getPayloadSize(), remainders[tid].getBpos(), 0);
|
||||||
}
|
}
|
||||||
remainders[tid].len = 0;
|
remainders[tid].clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (offsetInPes < realPayloadSize) {
|
while (offsetInPes < realPayloadSize) {
|
||||||
|
@ -506,125 +664,129 @@ namespace TS {
|
||||||
}
|
}
|
||||||
outPackets[tid].push_back(DTSC::Packet());
|
outPackets[tid].push_back(DTSC::Packet());
|
||||||
outPackets[tid].back().genericFill(timeStamp, timeOffset, tid, pesPayload, realPayloadSize, bPos, 0);
|
outPackets[tid].back().genericFill(timeStamp, timeOffset, tid, pesPayload, realPayloadSize, bPos, 0);
|
||||||
|
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (pidToCodec[tid] == H264 || pidToCodec[tid] == H265) {
|
if (pidToCodec[tid] == H264 || pidToCodec[tid] == H265) {
|
||||||
//Convert from annex b
|
//loop through scanAnnexB until startcode is found, if packetPointer equals NULL, then read next PES packet
|
||||||
char * parsedData = (char*)malloc(realPayloadSize * 2);
|
bool completePES = false;
|
||||||
|
const char * packetPtr;
|
||||||
|
const char * nalEnd;
|
||||||
|
bool checkForKeyFrame = true;
|
||||||
bool isKeyFrame = false;
|
bool isKeyFrame = false;
|
||||||
unsigned long parsedSize = nalu::fromAnnexB(pesPayload, realPayloadSize, parsedData);
|
int nalRemove = 0;
|
||||||
std::deque<nalu::nalData> nalInfo;
|
bool clearKey = false;
|
||||||
if (pidToCodec[tid] == H264) {
|
|
||||||
nalInfo = h264::analysePackets(parsedData, parsedSize);
|
nalu::scanAnnexB(pesPayload, realPayloadSize, packetPtr);
|
||||||
}
|
// std::cerr << "\t\tNew PES Packet" << std::endl;
|
||||||
if (pidToCodec[tid] == H265){
|
while (!completePES) {
|
||||||
nalInfo = h265::analysePackets(parsedData, parsedSize);
|
|
||||||
}
|
if (packetPtr) {
|
||||||
int dataOffset = 0;
|
//when startcode is found, check if we were already constructing a packet.
|
||||||
bool firstSlice = true;
|
if (!partialBuffer[tid].empty()) {
|
||||||
for (std::deque<nalu::nalData>::iterator it = nalInfo.begin(); it != nalInfo.end(); it++){
|
parseNal(tid, partialBuffer[tid].c_str(), partialBuffer[tid].c_str() + partialBuffer[tid].length(), isKeyFrame);
|
||||||
if (pidToCodec[tid] == H264){
|
|
||||||
switch (it->nalType){
|
|
||||||
case 0x01: {
|
|
||||||
if (firstSlice) {
|
|
||||||
firstSlice = false;
|
|
||||||
if (!isKeyFrame){
|
|
||||||
char * data = parsedData + dataOffset + 4;
|
|
||||||
Utils::bitstream bs;
|
|
||||||
for (size_t i = 1; i < 10 && i < it->nalSize; i++) {
|
|
||||||
if (i + 2 < it->nalSize && (memcmp(data + i, "\000\000\003", 3) == 0)) { //Emulation prevention bytes
|
|
||||||
bs.append(data + i, 2);
|
|
||||||
i += 2;
|
|
||||||
} else {
|
} else {
|
||||||
bs.append(data + i, 1);
|
parseNal(tid, pesPayload, packetPtr, isKeyFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isKeyFrame || clearKey) {
|
||||||
|
clearKey = true;
|
||||||
}
|
}
|
||||||
bs.getExpGolomb();//Discard first_mb_in_slice
|
|
||||||
uint64_t sliceType = bs.getUExpGolomb();
|
nalEnd = nalu::nalEndPosition(pesPayload, packetPtr - pesPayload);
|
||||||
if (sliceType == 2 || sliceType == 4 || sliceType == 7 || sliceType == 9){
|
nalRemove = packetPtr - nalEnd;
|
||||||
isKeyFrame = true;
|
|
||||||
|
if (firstPacketFound && checkForKeyFrame) {
|
||||||
|
checkForKeyFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!buildPacket[tid] && !firstPacketFound) {
|
||||||
|
//clean start
|
||||||
|
//remove everything before this point as this is the very first hal packet.
|
||||||
|
if (partialBuffer[tid].empty() && !firstPacketFound) { //if buffer is empty
|
||||||
|
//very first packet, check for keyframe
|
||||||
|
checkForKeyFrame = true;
|
||||||
}
|
}
|
||||||
}
|
firstPacketFound = true;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x05: {
|
|
||||||
isKeyFrame = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x07: {
|
|
||||||
if (threaded){
|
|
||||||
globalSem.wait();
|
|
||||||
}
|
|
||||||
spsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize);
|
|
||||||
if (threaded){
|
|
||||||
globalSem.post();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x08: {
|
|
||||||
if (threaded){
|
|
||||||
globalSem.wait();
|
|
||||||
}
|
|
||||||
ppsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize);
|
|
||||||
if (threaded){
|
|
||||||
globalSem.post();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (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: {
|
|
||||||
if (threaded){
|
|
||||||
globalSem.wait();
|
|
||||||
}
|
|
||||||
hevcInfo[tid].addUnit(parsedData + dataOffset);
|
|
||||||
if (threaded){
|
|
||||||
globalSem.post();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataOffset += 4 + it->nalSize;
|
|
||||||
}
|
|
||||||
if (threaded){
|
|
||||||
globalSem.wait();
|
|
||||||
}
|
|
||||||
outPackets[tid].push_back(DTSC::Packet());
|
|
||||||
outPackets[tid].back().genericFill(timeStamp, timeOffset, tid, parsedData, parsedSize, bPos, isKeyFrame);
|
|
||||||
if (threaded){
|
|
||||||
globalSem.post();
|
|
||||||
}
|
|
||||||
free(parsedData);
|
|
||||||
}
|
|
||||||
//We are done with the realpayload size, reverse calculation so we know the correct offset increase.
|
|
||||||
if (pidToCodec[tid] == AAC){
|
|
||||||
realPayloadSize += (3 + pesHeader[8]);
|
|
||||||
} else {
|
} else {
|
||||||
realPayloadSize += (9 + pesHeader[8]);
|
if (!buildPacket[tid]) { //when no data in packet -> genericFill
|
||||||
|
buildPacket[tid].genericFill(timeStamp, timeOffset, tid, "\000\000\000\002\011\360", 6, bPos, true);
|
||||||
}
|
}
|
||||||
offset += realPayloadSize + 6;
|
|
||||||
|
//if the timestamp differs from current PES timestamp, send the previous packet out and fill a new one.
|
||||||
|
if (buildPacket[tid].getTime() != timeStamp) {
|
||||||
|
//next packet's timestamp differs from current timestamp, add hal packet to buildpacket and send it out.
|
||||||
|
if (!partialBuffer[tid].empty()) {
|
||||||
|
//std::cerr << "append remaining data to partialbuffer" << std::endl;
|
||||||
|
buildPacket[tid].appendNal(partialBuffer[tid].c_str(), partialBuffer[tid].length(),
|
||||||
|
partialBuffer[tid].length() + (packetPtr - pesPayload) - nalRemove);
|
||||||
|
buildPacket[tid].appendData(pesPayload, (packetPtr - pesPayload) - nalRemove);
|
||||||
|
if (clearKey) {
|
||||||
|
buildPacket[tid].clearKeyFrame();
|
||||||
}
|
}
|
||||||
free(payload);
|
|
||||||
|
outPackets[tid].push_back(buildPacket[tid]);
|
||||||
|
buildPacket[tid].null();
|
||||||
|
buildPacket[tid].genericFill(timeStamp, timeOffset, tid, "\000\000\000\002\011\360", 6, bPos, true);
|
||||||
|
|
||||||
|
partialBuffer[tid].clear();
|
||||||
|
} else {
|
||||||
|
if (clearKey) {
|
||||||
|
buildPacket[tid].clearKeyFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
outPackets[tid].push_back(buildPacket[tid]);
|
||||||
|
buildPacket[tid].null();
|
||||||
|
buildPacket[tid].genericFill(timeStamp, timeOffset, tid, "\000\000\000\002\011\360", 6, bPos, true);
|
||||||
|
|
||||||
|
buildPacket[tid].appendNal(pesPayload, (packetPtr - pesPayload) - nalRemove, (packetPtr - pesPayload) - nalRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//we have a partial packet which belongs to the previous packet in partialBuffer.
|
||||||
|
if (!partialBuffer[tid].empty()) { //if buffer is used
|
||||||
|
buildPacket[tid].appendNal(partialBuffer[tid].c_str(), partialBuffer[tid].length(),
|
||||||
|
partialBuffer[tid].length() + (packetPtr - pesPayload) - nalRemove);
|
||||||
|
buildPacket[tid].appendData(pesPayload, (packetPtr - pesPayload) - nalRemove);
|
||||||
|
partialBuffer.clear();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//hal packet at first position
|
||||||
|
// buildPacket[tid].appendData(pesPayload, packetPtr-pesPayload); //append part before the startcode.
|
||||||
|
buildPacket[tid].appendNal(pesPayload, (packetPtr - pesPayload) - nalRemove, (packetPtr - pesPayload) - nalRemove); //append part before the startcode.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
realPayloadSize -= ((packetPtr - pesPayload) + 3); //decrease the total size
|
||||||
|
pesPayload = packetPtr + 3;
|
||||||
|
} else { //no startcode found...
|
||||||
|
if (partialBuffer[tid].empty()) {
|
||||||
|
partialBuffer[tid].assign(pesPayload, realPayloadSize);
|
||||||
|
} else {
|
||||||
|
partialBuffer[tid].append(pesPayload, realPayloadSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
completePES = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nalu::scanAnnexB(pesPayload, realPayloadSize, packetPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stream::getPacket(unsigned long tid, DTSC::Packet & pack) {
|
void Stream::getPacket(unsigned long tid, DTSC::Packet & pack) {
|
||||||
|
@ -674,17 +836,108 @@ namespace TS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Stream::parseNal(uint32_t tid, const char * pesPayload, const char * packetPtr, bool & isKeyFrame) {
|
||||||
|
//bool isKeyFrame = false;
|
||||||
|
//const char * packetPtr;
|
||||||
|
bool firstSlice = true;
|
||||||
|
char typeNal;
|
||||||
|
|
||||||
|
isKeyFrame = false;
|
||||||
|
typeNal = pesPayload[0] & 0x1F;
|
||||||
|
|
||||||
|
if (pidToCodec[tid] == H264) {
|
||||||
|
switch (typeNal) {
|
||||||
|
case 0x01: {
|
||||||
|
if (firstSlice) {
|
||||||
|
firstSlice = false;
|
||||||
|
if (!isKeyFrame) {
|
||||||
|
Utils::bitstream bs;
|
||||||
|
for (size_t i = 1; i < 10 && i < (packetPtr - pesPayload); i++) {
|
||||||
|
if (i + 2 < (packetPtr - pesPayload) && (memcmp(pesPayload + i, "\000\000\003", 3) == 0)) { //Emulation prevention bytes
|
||||||
|
bs.append(pesPayload + i, 2);
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
bs.append(pesPayload + i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bs.getExpGolomb();//Discard first_mb_in_slice
|
||||||
|
uint64_t sliceType = bs.getUExpGolomb();
|
||||||
|
if (sliceType == 2 || sliceType == 4 || sliceType == 7 || sliceType == 9) {
|
||||||
|
isKeyFrame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x05: {
|
||||||
|
isKeyFrame = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x07: {
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.wait();
|
||||||
|
}
|
||||||
|
spsInfo[tid] = std::string(pesPayload, (packetPtr - pesPayload));
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x08: {
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.wait();
|
||||||
|
}
|
||||||
|
ppsInfo[tid] = std::string(pesPayload, (packetPtr - pesPayload));
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (pidToCodec[tid] == H265) {
|
||||||
|
switch (typeNal) {
|
||||||
|
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: {
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.wait();
|
||||||
|
}
|
||||||
|
hevcInfo[tid].addUnit((char *)pesPayload);//may i convert to (char *)?
|
||||||
|
if (threaded) {
|
||||||
|
globalSem.post();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Stream::getEarliestPacket(DTSC::Packet & pack) {
|
void Stream::getEarliestPacket(DTSC::Packet & pack) {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.wait();
|
globalSem.wait();
|
||||||
}
|
}
|
||||||
pack.null();
|
pack.null();
|
||||||
if (!hasPacketOnEachTrack()){
|
|
||||||
if (threaded){
|
|
||||||
globalSem.post();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long packTime = 0xFFFFFFFFull;
|
unsigned long packTime = 0xFFFFFFFFull;
|
||||||
unsigned long packTrack = 0;
|
unsigned long packTrack = 0;
|
||||||
|
@ -699,32 +952,46 @@ namespace TS {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(packTrack){
|
||||||
getPacket(packTrack, pack);
|
getPacket(packTrack, pack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Stream::initializeMetadata(DTSC::Meta & meta, unsigned long tid) {
|
void Stream::initializeMetadata(DTSC::Meta & meta, unsigned long tid, unsigned long mappingId) {
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.wait();
|
globalSem.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long mId = mappingId;
|
||||||
|
|
||||||
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 (tid && it->first != tid) {
|
if (tid && it->first != tid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (meta.tracks.count(it->first) && meta.tracks[it->first].codec.size()){continue;}
|
|
||||||
|
if (mId == 0) {
|
||||||
|
mId = it->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta.tracks.count(mId) && meta.tracks[mId].codec.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (it->second) {
|
switch (it->second) {
|
||||||
case H264: {
|
case H264: {
|
||||||
if (!spsInfo.count(it->first) || !ppsInfo.count(it->first)) {
|
if (!spsInfo.count(it->first) || !ppsInfo.count(it->first)) {
|
||||||
MEDIUM_MSG("Aborted meta fill for h264 track %lu: no SPS/PPS", it->first);
|
MEDIUM_MSG("Aborted meta fill for h264 track %lu: no SPS/PPS", it->first);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
meta.tracks[it->first].type = "video";
|
meta.tracks[mId].type = "video";
|
||||||
meta.tracks[it->first].codec = "H264";
|
meta.tracks[mId].codec = "H264";
|
||||||
meta.tracks[it->first].trackID = it->first;
|
meta.tracks[mId].trackID = mId;
|
||||||
|
std::string tmpBuffer = spsInfo[it->first];
|
||||||
h264::sequenceParameterSet sps(spsInfo[it->first].data(), spsInfo[it->first].size());
|
h264::sequenceParameterSet sps(spsInfo[it->first].data(), spsInfo[it->first].size());
|
||||||
h264::SPSMeta spsChar = sps.getCharacteristics();
|
h264::SPSMeta spsChar = sps.getCharacteristics();
|
||||||
meta.tracks[it->first].width = spsChar.width;
|
meta.tracks[mId].width = spsChar.width;
|
||||||
meta.tracks[it->first].height = spsChar.height;
|
meta.tracks[mId].height = spsChar.height;
|
||||||
meta.tracks[it->first].fpks = spsChar.fps * 1000;
|
meta.tracks[mId].fpks = spsChar.fps * 1000;
|
||||||
MP4::AVCC avccBox;
|
MP4::AVCC avccBox;
|
||||||
avccBox.setVersion(1);
|
avccBox.setVersion(1);
|
||||||
avccBox.setProfile(spsInfo[it->first][1]);
|
avccBox.setProfile(spsInfo[it->first][1]);
|
||||||
|
@ -734,11 +1001,7 @@ namespace TS {
|
||||||
avccBox.setSPS(spsInfo[it->first]);
|
avccBox.setSPS(spsInfo[it->first]);
|
||||||
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[mId].init = std::string(avccBox.payload(), avccBox.payloadSize());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case H265: {
|
case H265: {
|
||||||
|
@ -746,17 +1009,17 @@ namespace TS {
|
||||||
MEDIUM_MSG("Aborted meta fill for hevc track %lu: no info nal unit", it->first);
|
MEDIUM_MSG("Aborted meta fill for hevc track %lu: no info nal unit", it->first);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
meta.tracks[it->first].type = "video";
|
meta.tracks[mId].type = "video";
|
||||||
meta.tracks[it->first].codec = "HEVC";
|
meta.tracks[mId].codec = "HEVC";
|
||||||
meta.tracks[it->first].trackID = it->first;
|
meta.tracks[mId].trackID = mId;
|
||||||
meta.tracks[it->first].init = hevcInfo[it->first].generateHVCC();
|
meta.tracks[mId].init = hevcInfo[it->first].generateHVCC();
|
||||||
int pmtCount = associationTable.getProgramCount();
|
int pmtCount = associationTable.getProgramCount();
|
||||||
for (int i = 0; i < pmtCount; i++) {
|
for (int i = 0; i < pmtCount; i++) {
|
||||||
int pid = associationTable.getProgramPID(i);
|
int pid = associationTable.getProgramPID(i);
|
||||||
ProgramMappingEntry entry = mappingTable[pid].getEntry(0);
|
ProgramMappingEntry entry = mappingTable[pid].getEntry(0);
|
||||||
while (entry) {
|
while (entry) {
|
||||||
if (entry.getElementaryPid() == tid) {
|
if (entry.getElementaryPid() == tid) {
|
||||||
meta.tracks[it->first].lang = ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).getLanguage();
|
meta.tracks[mId].lang = ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).getLanguage();
|
||||||
}
|
}
|
||||||
entry.advance();
|
entry.advance();
|
||||||
}
|
}
|
||||||
|
@ -764,33 +1027,33 @@ namespace TS {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ID3: {
|
case ID3: {
|
||||||
meta.tracks[it->first].type = "meta";
|
meta.tracks[mId].type = "meta";
|
||||||
meta.tracks[it->first].codec = "ID3";
|
meta.tracks[mId].codec = "ID3";
|
||||||
meta.tracks[it->first].trackID = it->first;
|
meta.tracks[mId].trackID = mId;
|
||||||
meta.tracks[it->first].init = metaInit[it->first];
|
meta.tracks[mId].init = metaInit[it->first];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AC3: {
|
case AC3: {
|
||||||
meta.tracks[it->first].type = "audio";
|
meta.tracks[mId].type = "audio";
|
||||||
meta.tracks[it->first].codec = "AC3";
|
meta.tracks[mId].codec = "AC3";
|
||||||
meta.tracks[it->first].trackID = it->first;
|
meta.tracks[mId].trackID = mId;
|
||||||
meta.tracks[it->first].size = 16;
|
meta.tracks[mId].size = 16;
|
||||||
///\todo Fix these 2 values
|
///\todo Fix these 2 values
|
||||||
meta.tracks[it->first].rate = 0;
|
meta.tracks[mId].rate = 0;
|
||||||
meta.tracks[it->first].channels = 0;
|
meta.tracks[mId].channels = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AAC: {
|
case AAC: {
|
||||||
meta.tracks[it->first].type = "audio";
|
meta.tracks[mId].type = "audio";
|
||||||
meta.tracks[it->first].codec = "AAC";
|
meta.tracks[mId].codec = "AAC";
|
||||||
meta.tracks[it->first].trackID = it->first;
|
meta.tracks[mId].trackID = mId;
|
||||||
meta.tracks[it->first].size = 16;
|
meta.tracks[mId].size = 16;
|
||||||
meta.tracks[it->first].rate = adtsInfo[it->first].getFrequency();
|
meta.tracks[mId].rate = adtsInfo[it->first].getFrequency();
|
||||||
meta.tracks[it->first].channels = adtsInfo[it->first].getChannelCount();
|
meta.tracks[mId].channels = adtsInfo[it->first].getChannelCount();
|
||||||
char audioInit[2];//5 bits object type, 4 bits frequency index, 4 bits channel index
|
char audioInit[2];//5 bits object type, 4 bits frequency index, 4 bits channel index
|
||||||
audioInit[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) | ((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1);
|
audioInit[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) | ((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1);
|
||||||
audioInit[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) | ((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3);
|
audioInit[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) | ((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3);
|
||||||
meta.tracks[it->first].init = std::string(audioInit, 2);
|
meta.tracks[mId].init = std::string(audioInit, 2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -801,12 +1064,12 @@ namespace TS {
|
||||||
ProgramMappingEntry entry = mappingTable[pid].getEntry(0);
|
ProgramMappingEntry entry = mappingTable[pid].getEntry(0);
|
||||||
while (entry) {
|
while (entry) {
|
||||||
if (entry.getElementaryPid() == tid) {
|
if (entry.getElementaryPid() == tid) {
|
||||||
meta.tracks[it->first].lang = ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).getLanguage();
|
meta.tracks[mId].lang = ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).getLanguage();
|
||||||
}
|
}
|
||||||
entry.advance();
|
entry.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MEDIUM_MSG("Initialized track %lu as %s %s", it->first, meta.tracks[it->first].codec.c_str(), meta.tracks[it->first].type.c_str());
|
MEDIUM_MSG("Initialized track %lu as %s %s", it->first, meta.tracks[mId].codec.c_str(), meta.tracks[mId].type.c_str());
|
||||||
}
|
}
|
||||||
if (threaded) {
|
if (threaded) {
|
||||||
globalSem.post();
|
globalSem.post();
|
||||||
|
|
|
@ -17,6 +17,28 @@ namespace TS {
|
||||||
ID3 = 0x15
|
ID3 = 0x15
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ADTSRemainder{
|
||||||
|
private:
|
||||||
|
char * data;
|
||||||
|
uint32_t max;
|
||||||
|
uint32_t now;
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t bpos;
|
||||||
|
public:
|
||||||
|
void setRemainder(const aac::adts & p, const void * source, const uint32_t avail, const uint32_t bPos);
|
||||||
|
|
||||||
|
ADTSRemainder();
|
||||||
|
~ADTSRemainder();
|
||||||
|
uint32_t getLength();
|
||||||
|
uint32_t getBpos();
|
||||||
|
uint32_t getTodo();
|
||||||
|
char* getData();
|
||||||
|
|
||||||
|
void append(const char *p,uint32_t pLen);
|
||||||
|
bool isComplete();
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
class Stream{
|
class Stream{
|
||||||
public:
|
public:
|
||||||
Stream(bool _threaded = false);
|
Stream(bool _threaded = false);
|
||||||
|
@ -26,25 +48,33 @@ namespace TS {
|
||||||
void parse(Packet & newPack, unsigned long long bytePos);
|
void parse(Packet & newPack, unsigned long long bytePos);
|
||||||
void parse(char * newPack, unsigned long long bytePos);
|
void parse(char * newPack, unsigned long long bytePos);
|
||||||
void parse(unsigned long tid);
|
void parse(unsigned long tid);
|
||||||
|
void parseNal(uint32_t tid, const char *pesPayload, const char * packetPtr, bool &isKeyFrame);
|
||||||
bool hasPacketOnEachTrack() const;
|
bool hasPacketOnEachTrack() const;
|
||||||
bool hasPacket(unsigned long tid) const;
|
bool hasPacket(unsigned long tid) const;
|
||||||
|
bool hasPacket() const;
|
||||||
void getPacket(unsigned long tid, DTSC::Packet & pack);
|
void getPacket(unsigned long tid, DTSC::Packet & pack);
|
||||||
void getEarliestPacket(DTSC::Packet & pack);
|
void getEarliestPacket(DTSC::Packet & pack);
|
||||||
void initializeMetadata(DTSC::Meta & meta, unsigned long tid = 0);
|
void initializeMetadata(DTSC::Meta & meta, unsigned long tid = 0, unsigned long mappingId = 0);
|
||||||
|
void partialClear();
|
||||||
void clear();
|
void clear();
|
||||||
|
void finish();
|
||||||
void eraseTrack(unsigned long tid);
|
void eraseTrack(unsigned long tid);
|
||||||
bool isDataTrack(unsigned long tid);
|
bool isDataTrack(unsigned long tid);
|
||||||
|
void parseBitstream(uint32_t tid, const char * pesPayload, uint32_t realPayloadSize,uint64_t timeStamp, int64_t timeOffset, uint64_t bPos);
|
||||||
std::set<unsigned long> getActiveTracks();
|
std::set<unsigned long> getActiveTracks();
|
||||||
private:
|
private:
|
||||||
unsigned long long lastPAT;
|
unsigned long long lastPAT;
|
||||||
ProgramAssociationTable associationTable;
|
ProgramAssociationTable associationTable;
|
||||||
|
std::map<unsigned long, ADTSRemainder> remainders;
|
||||||
|
|
||||||
|
bool firstPacketFound;
|
||||||
std::map<unsigned long, unsigned long long> lastPMT;
|
std::map<unsigned long, unsigned long long> lastPMT;
|
||||||
std::map<unsigned long, ProgramMappingTable> mappingTable;
|
std::map<unsigned long, ProgramMappingTable> mappingTable;
|
||||||
|
|
||||||
std::map<unsigned long, std::deque<Packet> > pesStreams;
|
std::map<unsigned long, std::deque<Packet> > pesStreams;
|
||||||
std::map<unsigned long, std::deque<unsigned long long> > pesPositions;
|
std::map<unsigned long, std::deque<unsigned long long> > pesPositions;
|
||||||
std::map<unsigned long, std::deque<DTSC::Packet> > outPackets;
|
std::map<unsigned long, std::deque<DTSC::Packet> > outPackets;
|
||||||
|
std::map<unsigned long, DTSC::Packet> buildPacket;
|
||||||
std::map<unsigned long, unsigned long> pidToCodec;
|
std::map<unsigned long, unsigned long> pidToCodec;
|
||||||
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;
|
||||||
|
@ -52,13 +82,13 @@ namespace TS {
|
||||||
std::map<unsigned long, h265::initData > hevcInfo;
|
std::map<unsigned long, h265::initData > hevcInfo;
|
||||||
std::map<unsigned long, std::string> metaInit;
|
std::map<unsigned long, std::string> metaInit;
|
||||||
std::map<unsigned long, std::string> descriptors;
|
std::map<unsigned long, std::string> descriptors;
|
||||||
|
std::map<unsigned long, std::string> partialBuffer;
|
||||||
mutable IPC::semaphore globalSem;
|
mutable IPC::semaphore globalSem;
|
||||||
|
|
||||||
bool threaded;
|
bool threaded;
|
||||||
|
|
||||||
std::set<unsigned long> pmtTracks;
|
std::set<unsigned long> pmtTracks;
|
||||||
|
|
||||||
void parsePES(unsigned long tid);
|
void parsePES(unsigned long tid, bool finished = false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
1216
src/input/input_hls.cpp
Normal file
1216
src/input/input_hls.cpp
Normal file
File diff suppressed because it is too large
Load diff
148
src/input/input_hls.h
Normal file
148
src/input/input_hls.h
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
#pragma once
|
||||||
|
#include "input.h"
|
||||||
|
#include <mist/dtsc.h>
|
||||||
|
#include <mist/nal.h>
|
||||||
|
#include <mist/ts_packet.h>
|
||||||
|
#include <mist/ts_stream.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
//#include <stdint.h>
|
||||||
|
|
||||||
|
#define BUFFERTIME 10
|
||||||
|
|
||||||
|
namespace Mist {
|
||||||
|
|
||||||
|
enum PlaylistType { VOD, LIVE, EVENT };
|
||||||
|
|
||||||
|
|
||||||
|
struct playListEntries
|
||||||
|
{
|
||||||
|
std::string filename;
|
||||||
|
uint64_t bytePos;
|
||||||
|
float duration;
|
||||||
|
unsigned int timestamp;
|
||||||
|
unsigned int wait;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Playlist {
|
||||||
|
public:
|
||||||
|
Playlist();
|
||||||
|
std::string codecs;
|
||||||
|
std::string video;
|
||||||
|
std::string audio;
|
||||||
|
std::string uri;
|
||||||
|
std::string uri_root;
|
||||||
|
|
||||||
|
std::string source;
|
||||||
|
const char *packetPtr;
|
||||||
|
|
||||||
|
int id;
|
||||||
|
bool playlistEnd;
|
||||||
|
int noChangeCount;
|
||||||
|
int version;
|
||||||
|
int targetDuration;
|
||||||
|
uint64_t media_sequence;
|
||||||
|
int lastFileIndex;
|
||||||
|
int waitTime;
|
||||||
|
bool vodLive;
|
||||||
|
PlaylistType playlistType;
|
||||||
|
std::deque<playListEntries> entries;
|
||||||
|
int entryCount;
|
||||||
|
int programId;
|
||||||
|
int bandwidth;
|
||||||
|
unsigned int lastTimestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct entryBuffer
|
||||||
|
{
|
||||||
|
int timestamp;
|
||||||
|
playListEntries entry;
|
||||||
|
int playlistIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
class inputHLS : public Input {
|
||||||
|
public:
|
||||||
|
inputHLS(Util::Config * cfg);
|
||||||
|
~inputHLS();
|
||||||
|
bool needsLock();
|
||||||
|
bool openStreamSource();
|
||||||
|
protected:
|
||||||
|
//Private Functions
|
||||||
|
|
||||||
|
unsigned int startTime;
|
||||||
|
PlaylistType playlistType;
|
||||||
|
int version;
|
||||||
|
int targetDuration;
|
||||||
|
int media_sequence;
|
||||||
|
bool endPlaylist;
|
||||||
|
int currentPlaylist;
|
||||||
|
|
||||||
|
bool initDone;
|
||||||
|
std::string init_source;
|
||||||
|
|
||||||
|
//std::vector<playListEntries> entries;
|
||||||
|
std::vector<Playlist> playlists;
|
||||||
|
//std::vector<int> pidMapping;
|
||||||
|
std::map<int,int> pidMapping;
|
||||||
|
std::map<int,int> pidMappingR;
|
||||||
|
|
||||||
|
std::string playlistFile;
|
||||||
|
std::string playlistRootPath;
|
||||||
|
std::vector<int> reloadNext;
|
||||||
|
|
||||||
|
|
||||||
|
bool liveStream;
|
||||||
|
int currentIndex;
|
||||||
|
std::string currentFile;
|
||||||
|
std::ifstream in;
|
||||||
|
bool isUrl;
|
||||||
|
|
||||||
|
TS::Stream tsStream;///<Used for parsing the incoming ts stream
|
||||||
|
bool pushing;
|
||||||
|
Socket::UDPConnection udpCon;
|
||||||
|
std::string udpDataBuffer;
|
||||||
|
Socket::Connection conn;
|
||||||
|
TS::Packet tsBuf;
|
||||||
|
|
||||||
|
int getFirstPlaylistToReload();
|
||||||
|
|
||||||
|
int firstSegment();
|
||||||
|
bool getNextSegment();
|
||||||
|
void readPMT();
|
||||||
|
bool setup();
|
||||||
|
bool preSetup();
|
||||||
|
bool readHeader();
|
||||||
|
void getNext(bool smart = true);
|
||||||
|
void seek(int seekTime);
|
||||||
|
void trackSelect(std::string trackSpec);
|
||||||
|
FILE * inFile;
|
||||||
|
FILE * tsFile;
|
||||||
|
bool openURL(std::string urlString, Playlist &p);
|
||||||
|
|
||||||
|
|
||||||
|
void printContent();
|
||||||
|
void printBuffer();
|
||||||
|
bool readIndex();
|
||||||
|
bool initPlaylist(std::string uri);
|
||||||
|
bool readPlaylist(std::string uri);
|
||||||
|
bool reloadPlaylist(Playlist &p);
|
||||||
|
bool readNextFile();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void parseStreamHeader();
|
||||||
|
void addEntryToPlaylist(Playlist &p, std::string filename, float duration, uint64_t &totalBytes);
|
||||||
|
|
||||||
|
int getMappedTrackId(int id);
|
||||||
|
int getMappedTrackPlaylist(int id);
|
||||||
|
int getOriginalTrackId(int playlistId, int id);
|
||||||
|
int getEntryId(int playlistId, uint64_t bytePos);
|
||||||
|
int cleanLine(std::string &s);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Mist::inputHLS mistIn;
|
||||||
|
|
|
@ -130,7 +130,6 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///Live Setup of TS Input
|
///Live Setup of TS Input
|
||||||
bool inputTS::setup() {
|
bool inputTS::setup() {
|
||||||
const std::string & inpt = config->getString("input");
|
const std::string & inpt = config->getString("input");
|
||||||
|
@ -205,7 +204,17 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
myMeta.update(headerPack);
|
myMeta.update(headerPack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DTSC::Packet headerPack;
|
||||||
|
tsStream.getEarliestPacket(headerPack);
|
||||||
|
|
||||||
|
while (headerPack) {
|
||||||
|
if (!myMeta.tracks.count(headerPack.getTrackId()) || !myMeta.tracks[headerPack.getTrackId()].codec.size()) {
|
||||||
|
tsStream.initializeMetadata(myMeta, headerPack.getTrackId());
|
||||||
|
}
|
||||||
|
myMeta.update(headerPack);
|
||||||
|
tsStream.getEarliestPacket(headerPack);
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(inFile, 0, SEEK_SET);
|
fseek(inFile, 0, SEEK_SET);
|
||||||
|
@ -230,9 +239,6 @@ namespace Mist {
|
||||||
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 (!feof(inFile)) {
|
|
||||||
getNext();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedTracks.size() == 1) {
|
if (selectedTracks.size() == 1) {
|
||||||
|
@ -264,14 +270,12 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Clear leaves the PMT in place
|
//Clear leaves the PMT in place
|
||||||
tsStream.clear();
|
tsStream.partialClear();
|
||||||
|
|
||||||
|
|
||||||
//Restore original file position
|
//Restore original file position
|
||||||
if (fseek(inFile, bpos, SEEK_SET)) {
|
if (fseek(inFile, bpos, SEEK_SET)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///Seeks to a specific time
|
///Seeks to a specific time
|
||||||
|
|
Loading…
Add table
Reference in a new issue