Recording, HLS Push, UDP (Multicast) Input, Threaded TS

This commit is contained in:
Erik Zandvliet 2016-01-28 15:00:25 +01:00 committed by Thulinma
parent 1c3e143709
commit c25a533729
29 changed files with 1809 additions and 815 deletions

View file

@ -4,48 +4,148 @@
#include "h265.h"
#include "nal.h"
#include "mp4_generic.h"
#include <sys/stat.h>
namespace TS {
Stream::Stream(bool _threaded){
threaded = _threaded;
if (threaded){
globalSem.open("MstTSInputLock", O_CREAT | O_EXCL | O_RDWR, ACCESSPERMS, 1);
if (!globalSem) {
globalSem.open("MstTSInputLock", O_CREAT | O_RDWR, ACCESSPERMS, 1);
}
if (!globalSem) {
FAIL_MSG("Creating semaphore failed: %s", strerror(errno));
threaded = false;
DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return;
}
}
}
void Stream::parse(char * newPack, unsigned long long bytePos) {
Packet newPacket;
newPacket.FromPointer(newPack);
parse(newPacket, bytePos);
}
void Stream::clear(){
if (threaded){
globalSem.wait();
}
pesStreams.clear();
pesPositions.clear();
payloadSize.clear();
outPackets.clear();
if (threaded){
globalSem.post();
}
}
void Stream::add(char * newPack, unsigned long long bytePos) {
Packet newPacket;
newPacket.FromPointer(newPack);
add(newPacket, bytePos);
}
void Stream::add(Packet & newPack, unsigned long long bytePos) {
if (threaded){
globalSem.wait();
}
int tid = newPack.getPID();
pesStreams[tid].push_back(newPack);
pesPositions[tid].push_back(bytePos);
if (threaded){
globalSem.post();
}
}
bool Stream::isDataTrack(unsigned long tid){
if (tid == 0){
return false;
}
if (threaded){
globalSem.wait();
}
bool result = !pmtTracks.count(tid);
if (threaded){
globalSem.post();
}
return result;
}
void Stream::parse(Packet & newPack, unsigned long long bytePos) {
int tid = newPack.getPID();
void Stream::parse(unsigned long tid) {
if (threaded){
globalSem.wait();
}
if (!pesStreams.count(tid) || pesStreams[tid].size() == 0){
if (threaded){
globalSem.post();
}
return;
}
std::deque<Packet> & trackPackets = pesStreams[tid];
if (threaded){
globalSem.post();
}
//Handle PAT packets
if (tid == 0){
associationTable = newPack;
pmtTracks.clear();
///\todo Keep track of updates in PAT instead of keeping only the last PAT as a reference
if (threaded){
globalSem.wait();
}
associationTable = trackPackets.back();
lastPAT = Util::bootSecs();
if (threaded){
globalSem.post();
}
int pmtCount = associationTable.getProgramCount();
for (int i = 0; i < pmtCount; i++){
pmtTracks.insert(associationTable.getProgramPID(i));
}
if (threaded){
globalSem.wait();
}
pesStreams.erase(0);
pesPositions.erase(0);
if (threaded){
globalSem.post();
}
return;
}
//If we are here, the packet is not a PAT.
//First check if it is listed in the PAT as a PMT track.
int pmtCount = associationTable.getProgramCount();
//Handle PMT packets
if (pmtTracks.count(tid)){
mappingTable[tid] = newPack;
///\todo Keep track of updates in PMT instead of keeping only the last PMT per program as a reference
if (threaded){
globalSem.wait();
}
mappingTable[tid] = trackPackets.back();
lastPMT[tid] = Util::bootSecs();
if (threaded){
globalSem.post();
}
ProgramMappingEntry entry = mappingTable[tid].getEntry(0);
while (entry){
unsigned long pid = entry.getElementaryPid();
switch(entry.getStreamType()){
unsigned long sType = entry.getStreamType();
switch(sType){
case H264:
case AAC:
case HEVC:
case H265:
case AC3:
if (!pidToCodec.count(pid)){
pidToCodec[pid] = entry.getStreamType();
case ID3:
pidToCodec[pid] = sType;
if (sType == ID3){
metaInit[pid] = std::string(entry.getESInfo(), entry.getESInfoLength());
}
break;
default:
@ -53,57 +153,107 @@ namespace TS {
}
entry.advance();
}
if (threaded){
globalSem.wait();
}
pesStreams.erase(tid);
pesPositions.erase(tid);
if (threaded){
globalSem.post();
}
return;
}
//If it is not a PMT, check the list of all PMTs to see if this is a new PES track.
bool inPMT = false;
for (std::map<unsigned long, ProgramMappingTable>::iterator it = mappingTable.begin(); it!= mappingTable.end(); it++){
ProgramMappingEntry entry = it->second.getEntry(0);
while (entry){
if (tid == entry.getElementaryPid()){
inPMT = true;
break;
}
entry.advance();
}
if (inPMT){
break;
}
if (threaded){
globalSem.wait();
}
if (!inPMT){
HIGH_MSG("Encountered a packet on track %d, but the track is not registered in any PMT", tid);
return;
bool parsePes = false;
int packNum = 1;
std::deque<Packet> & inStream = pesStreams[tid];
std::deque<Packet>::iterator curPack = inStream.begin();
curPack++;
while (curPack != inStream.end() && !curPack->getUnitStart()){
curPack++;
packNum++;
}
pesStreams[tid].push_back(newPack);
pesPositions[tid].push_back(bytePos);
if (!newPack.getUnitStart() || pesStreams[tid].size() == 1){
payloadSize[tid] += newPack.getPayloadLength();
if (curPack != inStream.end()){
parsePes = true;
}
parsePES(tid);
if (threaded){
globalSem.post();
}
if (parsePes){
parsePES(tid);
}
}
void Stream::parse(Packet & newPack, unsigned long long bytePos) {
add(newPack, bytePos);
int tid = newPack.getPID();
parse(tid);
}
bool Stream::hasPacketOnEachTrack() const {
if (!pidToCodec.size()){
return false;
if (threaded){
globalSem.wait();
}
if (outPackets.size() != pidToCodec.size()){
if (!pidToCodec.size() || pidToCodec.size() != outPackets.size()){
if (threaded){
globalSem.post();
}
return false;
}
for (std::map<unsigned long, unsigned long>::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){
if (!outPackets.count(it->first) || !outPackets.at(it->first).size()){
if (!hasPacket(it->first)){
if (threaded){
globalSem.post();
}
return false;
}
}
if (threaded){
globalSem.post();
}
return true;
}
bool Stream::hasPacket(unsigned long tid) const {
if (threaded){
globalSem.wait();
}
if (!pesStreams.count(tid)){
if (threaded){
globalSem.post();
}
return false;
}
if (outPackets.count(tid) && outPackets.at(tid).size()){
if (threaded){
globalSem.post();
}
return true;
}
std::deque<Packet>::const_iterator curPack = pesStreams.at(tid).begin();
curPack++;
while (curPack != pesStreams.at(tid).end() && !curPack->getUnitStart()){
curPack++;
}
if (curPack != pesStreams.at(tid).end()){
if (threaded){
globalSem.post();
}
return true;
}
if (threaded){
globalSem.post();
}
return false;
}
@ -119,32 +269,63 @@ namespace TS {
}
void Stream::parsePES(unsigned long tid){
if (threaded){
globalSem.wait();
}
std::deque<Packet> & inStream = pesStreams[tid];
std::deque<unsigned long long> & inPositions = pesPositions[tid];
if (inStream.size() == 1){
if (threaded){
globalSem.post();
}
return;
}
if (!inStream.back().getUnitStart()){
//Find number of packets before unit Start
int packNum = 1;
std::deque<Packet>::iterator curPack = inStream.begin();
curPack++;
while (curPack != inStream.end() && !curPack->getUnitStart()){
curPack++;
packNum++;
}
if (curPack == inStream.end()){
if (threaded){
globalSem.post();
}
return;
}
unsigned long long bPos = inPositions.front();
//Create a buffer for the current PES, and remove it from the pesStreams buffer.
int paySize = payloadSize[tid];
char * payload = (char*)malloc(paySize);
int offset = 0;
int packNum = inStream.size() - 1;
std::deque<Packet>::iterator curPack = inStream.begin();
int paySize = 0;
curPack = inStream.begin();
for (int i = 0; i < packNum; i++){
memcpy(payload + offset, curPack->getPayload(), curPack->getPayloadLength());
offset += curPack->getPayloadLength();
paySize += curPack->getPayloadLength();
curPack++;
}
char * payload = (char*)malloc(paySize);
paySize = 0;
curPack = inStream.begin();
int lastCtr = curPack->getContinuityCounter() - 1;
for (int i = 0; i < packNum; i++){
if (curPack->getContinuityCounter() - lastCtr != 1 && curPack->getContinuityCounter()){
INFO_MSG("Parsing a pes on track %d, missed %d packets", tid, curPack->getContinuityCounter() - lastCtr - 1);
}
lastCtr = curPack->getContinuityCounter();
memcpy(payload + paySize, curPack->getPayload(), curPack->getPayloadLength());
paySize += curPack->getPayloadLength();
curPack++;
}
inStream.erase(inStream.begin(), curPack);
inPositions.erase(inPositions.begin(), inPositions.begin() + packNum);
if (threaded){
globalSem.post();
}
//Parse the PES header
offset = 0;
int offset = 0;
while(offset < paySize){
const char * pesHeader = payload + offset;
@ -203,6 +384,9 @@ namespace TS {
//Parse all the ADTS packets
unsigned long offsetInPes = 0;
unsigned long samplesRead = 0;
if (threaded){
globalSem.wait();
}
while (offsetInPes < realPayloadSize){
outPackets[tid].push_back(DTSC::Packet());
aac::adts adtsPack(pesPayload + offsetInPes, realPayloadSize - offsetInPes);
@ -213,10 +397,19 @@ namespace TS {
samplesRead += adtsPack.getSampleCount();
offsetInPes += adtsPack.getHeaderSize() + adtsPack.getPayloadSize();
}
if (threaded){
globalSem.post();
}
}
if (pidToCodec[tid] == AC3){
if (pidToCodec[tid] == ID3 || pidToCodec[tid] == AC3){
if (threaded){
globalSem.wait();
}
outPackets[tid].push_back(DTSC::Packet());
outPackets[tid].back().genericFill(timeStamp, timeOffset, tid, pesPayload, realPayloadSize, bPos, 0);
if (threaded){
globalSem.post();
}
}
if (pidToCodec[tid] == H264 || pidToCodec[tid] == HEVC || pidToCodec[tid] == H265){
//Convert from annex b
@ -239,11 +432,23 @@ namespace TS {
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;
@ -264,7 +469,13 @@ namespace TS {
case 32:
case 33:
case 34: {
if (threaded){
globalSem.wait();
}
hevcInfo[tid].addUnit(parsedData + dataOffset);
if (threaded){
globalSem.post();
}
break;
}
default: break;
@ -272,8 +483,14 @@ namespace TS {
}
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.
@ -285,7 +502,6 @@ namespace TS {
offset += realPayloadSize + 6;
}
free(payload);
payloadSize[tid] = inStream.front().getPayloadLength();
}
void Stream::getPacket(unsigned long tid, DTSC::Packet & pack) {
@ -295,17 +511,55 @@ namespace TS {
return;
}
if (threaded){
globalSem.wait();
}
bool packetReady = outPackets.count(tid) && outPackets[tid].size();
if (threaded){
globalSem.post();
}
if (!packetReady){
parse(tid);
}
if (threaded){
globalSem.wait();
}
packetReady = outPackets.count(tid) && outPackets[tid].size();
if (threaded){
globalSem.post();
}
if (!packetReady){
ERROR_MSG("Obtaining a packet on track %lu failed", tid);
return;
}
if (threaded){
globalSem.wait();
}
pack = outPackets[tid].front();
outPackets[tid].pop_front();
if (!outPackets[tid].size()){
outPackets.erase(tid);
}
if (threaded){
globalSem.post();
}
}
void Stream::getEarliestPacket(DTSC::Packet & pack){
if (threaded){
globalSem.wait();
}
pack.null();
if (!hasPacketOnEachTrack()){
if (threaded){
globalSem.post();
}
return;
}
@ -318,12 +572,21 @@ namespace TS {
packTime = it->second.front().getTime();
}
}
if (threaded){
globalSem.post();
}
getPacket(packTrack, pack);
}
void Stream::initializeMetadata(DTSC::Meta & meta) {
void Stream::initializeMetadata(DTSC::Meta & meta, unsigned long tid) {
if (threaded){
globalSem.wait();
}
for (std::map<unsigned long, unsigned long>::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){
if (tid && it->first != tid){
continue;
}
if (!meta.tracks.count(it->first) && it->second == H264){
if (!spsInfo.count(it->first) || !ppsInfo.count(it->first)){
continue;
@ -357,6 +620,12 @@ namespace TS {
meta.tracks[it->first].trackID = it->first;
meta.tracks[it->first].init = hevcInfo[it->first].generateHVCC();
}
if (!meta.tracks.count(it->first) && it->second == ID3){
meta.tracks[it->first].type = "meta";
meta.tracks[it->first].codec = "ID3";
meta.tracks[it->first].trackID = it->first;
meta.tracks[it->first].init = metaInit[it->first];
}
if (!meta.tracks.count(it->first) && it->second == AC3){
meta.tracks[it->first].type = "audio";
meta.tracks[it->first].codec = "AC3";
@ -379,5 +648,63 @@ namespace TS {
meta.tracks[it->first].init = std::string(audioInit, 2);
}
}
if (threaded){
globalSem.post();
}
}
std::set<unsigned long> Stream::getActiveTracks() {
if (threaded){
globalSem.wait();
}
std::set<unsigned long> result;
//Track 0 is always active
result.insert(0);
//IF PAT updated in the last 5 seconds, check for contents
if (Util::bootSecs() - lastPAT < 5){
int pmtCount = associationTable.getProgramCount();
//For each PMT
for (int i = 0; i < pmtCount; i++){
int pid = associationTable.getProgramPID(i);
//Add PMT track
result.insert(pid);
//IF PMT updated in last 5 seconds, check for contents
if (Util::bootSecs() - lastPMT[pid] < 5){
ProgramMappingEntry entry = mappingTable[pid].getEntry(0);
//Add all tracks in PMT
while (entry){
switch(entry.getStreamType()){
case H264:
case AAC:
case HEVC:
case H265:
case AC3:
case ID3:
result.insert(entry.getElementaryPid());
break;
default:
break;
}
entry.advance();
}
}
}
}
if (threaded){
globalSem.post();
}
return result;
}
void Stream::eraseTrack(unsigned long tid){
if (threaded){
globalSem.wait();
}
pesStreams.erase(tid);
pesPositions.erase(tid);
outPackets.erase(tid);
if (threaded){
globalSem.post();
}
}
}