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

@ -1,69 +0,0 @@
add_library ( mist SHARED
amf.cpp
amf.h
auth.cpp
auth.h
base64.cpp
base64.h
bitfields.cpp
bitfields.h
bitstream.cpp
bitstream.h
checksum.h
CMakeLists.txt
config.cpp
config.h
converter.cpp
converter.h
defines.h
dtsc.cpp
dtsc.h
dtscmeta.cpp
filesystem.cpp
filesystem.h
flv_tag.cpp
flv_tag.h
ftp.cpp
ftp.h
http_parser.cpp
http_parser.h
json.cpp
json.h
mp4_adobe.cpp
mp4_adobe.h
mp4.cpp
mp4_generic.cpp
mp4_generic.h
mp4.h
mp4_ms.cpp
mp4_ms.h
nal.cpp
nal.h
ogg.cpp
ogg.h
procs.cpp
procs.h
rtmpchunks.cpp
rtmpchunks.h
shared_memory.cpp
shared_memory.h
socket.cpp
socket.h
stream.cpp
stream.h
theora.cpp
theora.h
timing.cpp
timing.h
tinythread.cpp
tinythread.h
ts_packet.cpp
ts_packet.h
vorbis.cpp
vorbis.h
)
target_link_libraries( mist
-lpthread
-lrt
)

View file

@ -298,7 +298,7 @@ namespace DTSC {
int getSendLen();
void send(Socket::Connection & conn);
void writeTo(char *& p);
JSON::Value toJSON();
JSON::Value toJSON(bool skipBinary = false);
std::deque<Fragment> fragments;
std::deque<Key> keys;
std::deque<unsigned long> keySizes;

View file

@ -1772,43 +1772,46 @@ namespace DTSC {
}
///\brief Converts a track to a JSON::Value
JSON::Value Track::toJSON() {
JSON::Value Track::toJSON(bool skipBinary) {
JSON::Value result;
std::string tmp;
tmp.reserve(fragments.size() * PACKED_FRAGMENT_SIZE);
for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++) {
tmp.append(it->getData(), PACKED_FRAGMENT_SIZE);
if (!skipBinary) {
tmp.reserve(fragments.size() * PACKED_FRAGMENT_SIZE);
for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++) {
tmp.append(it->getData(), PACKED_FRAGMENT_SIZE);
}
result["fragments"] = tmp;
tmp = "";
tmp.reserve(keys.size() * PACKED_KEY_SIZE);
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
tmp.append(it->getData(), PACKED_KEY_SIZE);
}
result["keys"] = tmp;
tmp = "";
tmp.reserve(keySizes.size() * 4);
for (unsigned int i = 0; i < keySizes.size(); i++){
tmp += (char)((keySizes[i] >> 24));
tmp += (char)((keySizes[i] >> 16));
tmp += (char)((keySizes[i] >> 8));
tmp += (char)(keySizes[i]);
}
result["keysizes"] = tmp;
tmp = "";
tmp.reserve(parts.size() * 9);
for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
tmp.append(it->getData(), 9);
}
result["parts"] = tmp;
/*LTS-START*/
tmp = "";
tmp.reserve(ivecs.size() * 8);
for (std::deque<Ivec>::iterator it = ivecs.begin(); it != ivecs.end(); it++) {
tmp.append(it->getData(), 8);
}
result["ivecs"] = tmp;
/*LTS-END*/
result["init"] = init;
}
result["fragments"] = tmp;
tmp = "";
tmp.reserve(keys.size() * PACKED_KEY_SIZE);
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
tmp.append(it->getData(), PACKED_KEY_SIZE);
}
result["keys"] = tmp;
tmp = "";
tmp.reserve(keySizes.size() * 4);
for (unsigned int i = 0; i < keySizes.size(); i++){
tmp += (char)((keySizes[i] >> 24));
tmp += (char)((keySizes[i] >> 16));
tmp += (char)((keySizes[i] >> 8));
tmp += (char)(keySizes[i]);
}
result["keysizes"] = tmp;
tmp = "";
tmp.reserve(parts.size() * 9);
for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
tmp.append(it->getData(), 9);
}
result["parts"] = tmp;
/*LTS-START*/
tmp = "";
tmp.reserve(ivecs.size() * 8);
for (std::deque<Ivec>::iterator it = ivecs.begin(); it != ivecs.end(); it++) {
tmp.append(it->getData(), 8);
}
result["ivecs"] = tmp;
/*LTS-END*/
result["trackid"] = trackID;
result["firstms"] = (long long)firstms;
result["lastms"] = (long long)lastms;
@ -1818,7 +1821,6 @@ namespace DTSC {
}
result["codec"] = codec;
result["type"] = type;
result["init"] = init;
if (type == "audio") {
result["rate"] = rate;
result["size"] = size;

View file

@ -180,13 +180,11 @@ void HTTP::Parser::StartResponse(HTTP::Parser & request, Socket::Connection & co
StartResponse("200", "OK", request, conn, bufferAllChunks);
}
/// After receiving a header with this object, this function call will:
/// - Forward the headers to the 'to' Socket::Connection.
/// After receiving a header with this object, and after a call with SendResponse/SendRequest with this object, this function call will:
/// - Retrieve all the body from the 'from' Socket::Connection.
/// - Forward those contents as-is to the 'to' Socket::Connection.
/// It blocks until completed or either of the connections reaches an error state.
void HTTP::Parser::Proxy(Socket::Connection & from, Socket::Connection & to) {
SendResponse(url, method, to);
if (getChunks) {
unsigned int proxyingChunk = 0;
while (to.connected() && from.connected()) {

View file

@ -15,14 +15,21 @@
#include "bitfields.h"
#include "timing.h"
#if defined(__CYGWIN__) || defined(_WIN32)
#include <windows.h>
#include <aclapi.h>
#include <accctrl.h>
#endif
namespace IPC {
#if defined(__CYGWIN__) || defined(_WIN32)
static std::map<std::string, sharedPage> preservedPages;
void preservePage(std::string p){
void preservePage(std::string p) {
preservedPages[p].init(p, 0, false, false);
}
void releasePage(std::string p){
void releasePage(std::string p) {
preservedPages.erase(p);
}
#endif
@ -68,7 +75,7 @@ namespace IPC {
static void btohl(char * p, unsigned int & val) {
val = ((long)p[0] << 24) | ((long)p[1] << 16) | ((long)p[2] << 8) | p[3];
}
/// Reads a long long value of p in host order to val.
static void btohll(char * p, long long & val) {
val = ((long long)p[0] << 56) | ((long long)p[1] << 48) | ((long long)p[2] << 40) | ((long long)p[3] << 32) | ((long long)p[4] << 24) | ((long long)p[5] << 16) | ((long long)p[6] << 8) | p[7];
@ -108,7 +115,7 @@ namespace IPC {
#if defined(__CYGWIN__) || defined(_WIN32)
return mySem != 0;
#else
return mySem != SEM_FAILED;
return mySem && mySem != SEM_FAILED;
#endif
}
@ -122,23 +129,34 @@ namespace IPC {
void semaphore::open(const char * name, int oflag, mode_t mode, unsigned int value) {
close();
int timer = 0;
while (!(*this) && timer++ < 10){
while (!(*this) && timer++ < 10) {
#if defined(__CYGWIN__) || defined(_WIN32)
std::string semaName = "Global\\";
semaName += name;
if (oflag & O_CREAT){
if (oflag & O_EXCL){
if (oflag & O_CREAT) {
if (oflag & O_EXCL) {
//attempt opening, if succes, close handle and return false;
HANDLE tmpSem = OpenSemaphore(0, false, semaName.c_str());
if (tmpSem){
HANDLE tmpSem = OpenMutex(SYNCHRONIZE, false, semaName.c_str());
if (tmpSem) {
CloseHandle(tmpSem);
mySem = 0;
break;
}
}
mySem = CreateSemaphore(0, value, 1 , semaName.c_str());
}else{
mySem = OpenSemaphore(0, false, semaName.c_str());
SECURITY_ATTRIBUTES security = getSecurityAttributes();
mySem = CreateMutex(&security, true, semaName.c_str());
if (value){
ReleaseMutex(mySem);
}
} else {
mySem = OpenMutex(SYNCHRONIZE, false, semaName.c_str());
}
if (!(*this)) {
if (GetLastError() == ERROR_FILE_NOT_FOUND){//Error code 2
Util::wait(500);
} else {
break;
}
}
#else
if (oflag & O_CREAT) {
@ -146,17 +164,16 @@ namespace IPC {
} else {
mySem = sem_open(name, oflag);
}
#endif
if (!(*this)){
if (errno == ENOENT){
if (!(*this)) {
if (errno == ENOENT) {
Util::wait(500);
}else{
} else {
break;
}
}
#endif
}
if (!(*this)){
DEBUG_MSG(DLVL_VERYHIGH, "Attempt to open semaphore %s: %s", name, strerror(errno));
if (!(*this)) {
}
myName = (char *)name;
}
@ -177,7 +194,7 @@ namespace IPC {
void semaphore::post() {
if (*this) {
#if defined(__CYGWIN__) || defined(_WIN32)
ReleaseSemaphore(mySem, 1, 0);
ReleaseMutex(mySem);
#else
sem_post(mySem);
#endif
@ -203,7 +220,7 @@ namespace IPC {
int result;
#if defined(__CYGWIN__) || defined(_WIN32)
result = WaitForSingleObject(mySem, 0);//wait at most 1ms
if (result == 0x80){
if (result == 0x80) {
WARN_MSG("Consistency error caught on semaphore %s", myName.c_str());
result = 0;
}
@ -212,13 +229,13 @@ namespace IPC {
#endif
return (result == 0);
}
///\brief Tries to wait for the semaphore for a single second, returns true if successful, false otherwise
bool semaphore::tryWaitOneSecond() {
int result;
#if defined(__CYGWIN__) || defined(_WIN32)
result = WaitForSingleObject(mySem, 1000);//wait at most 1s
if (result == 0x80){
if (result == 0x80) {
WARN_MSG("Consistency error caught on semaphore %s", myName.c_str());
result = 0;
}
@ -268,12 +285,38 @@ namespace IPC {
}
#if defined(__CYGWIN__) || defined(_WIN32)
SECURITY_ATTRIBUTES semaphore::getSecurityAttributes() {
///\todo We really should clean this up sometime probably
///We currently have everything static, because the result basically depends on everything
static SECURITY_ATTRIBUTES result;
static bool resultValid = false;
static SECURITY_DESCRIPTOR securityDescriptor;
if (resultValid) {
return result;
}
InitializeSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION);
if (!SetSecurityDescriptorDacl(&securityDescriptor, TRUE, NULL, FALSE)){
FAIL_MSG("Failed to set pSecurityDescriptor: %u", GetLastError());
return result;
}
result.nLength = sizeof(SECURITY_ATTRIBUTES);
result.lpSecurityDescriptor = &securityDescriptor;
result.bInheritHandle = FALSE;
resultValid = true;
return result;
}
#endif
///brief Creates a shared page
///\param name_ The name of the page to be created
///\param len_ The size to make the page
///\param master_ Whether to create or merely open the page
///\param autoBackoff When only opening the page, wait for it to appear or fail
sharedPage::sharedPage(std::string name_, unsigned int len_, bool master_, bool autoBackoff){
sharedPage::sharedPage(std::string name_, unsigned int len_, bool master_, bool autoBackoff) {
handle = 0;
len = 0;
master = false;
@ -302,7 +345,7 @@ namespace IPC {
if (mapped && len) {
#if defined(__CYGWIN__) || defined(_WIN32)
//under Cygwin, the mapped location is shifted by 4 to contain the page size.
UnmapViewOfFile(mapped-4);
UnmapViewOfFile(mapped - 4);
#else
munmap(mapped, len);
#endif
@ -315,7 +358,7 @@ namespace IPC {
void sharedPage::close() {
unmap();
if (handle > 0) {
INSANE_MSG("Closing page %s in %s mode", name.c_str(), master?"master":"client");
INSANE_MSG("Closing page %s in %s mode", name.c_str(), master ? "master" : "client");
#if defined(__CYGWIN__) || defined(_WIN32)
CloseHandle(handle);
#else
@ -353,11 +396,11 @@ namespace IPC {
master = master_;
mapped = 0;
if (name.size()) {
INSANE_MSG("Opening page %s in %s mode %s auto-backoff", name.c_str(), master?"master":"client", autoBackoff?"with":"without");
INSANE_MSG("Opening page %s in %s mode %s auto-backoff", name.c_str(), master ? "master" : "client", autoBackoff ? "with" : "without");
#if defined(__CYGWIN__) || defined(_WIN32)
if (master) {
//Under cygwin, all pages are 4 bytes longer than claimed.
handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, len+4, name.c_str());
handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, len + 4, name.c_str());
} else {
int i = 0;
do {
@ -378,10 +421,10 @@ namespace IPC {
return;
}
//Under cygwin, the extra 4 bytes contain the real size of the page.
if (master){
((unsigned int*)mapped)[0] = len_;
}else{
len = ((unsigned int*)mapped)[0];
if (master) {
((unsigned int *)mapped)[0] = len_;
} else {
len = ((unsigned int *)mapped)[0];
}
//Now shift by those 4 bytes.
mapped += 4;
@ -401,7 +444,7 @@ namespace IPC {
}
}
if (handle == -1) {
if (!master_ && autoBackoff){
if (!master_ && autoBackoff) {
FAIL_MSG("shm_open for page %s failed: %s", name.c_str(), strerror(errno));
}
return;
@ -480,7 +523,7 @@ namespace IPC {
len = 0;
}
}
/// Unmaps, closes and unlinks (if master and name is set) the shared file.
void sharedFile::close() {
unmap();
@ -616,8 +659,8 @@ namespace IPC {
///\brief Sets the host of this connection
void statExchange::host(std::string name) {
if (name.size() < 16){
memset(data+32, 0, 16);
if (name.size() < 16) {
memset(data + 32, 0, 16);
}
memcpy(data + 32, name.c_str(), std::min((int)name.size(), 16));
}
@ -630,7 +673,7 @@ namespace IPC {
///\brief Sets the name of the stream this user is viewing
void statExchange::streamName(std::string name) {
size_t splitChar = name.find_first_of("+ ");
if (splitChar != std::string::npos){
if (splitChar != std::string::npos) {
name[splitChar] = '+';
}
memcpy(data + 48, name.c_str(), std::min((int)name.size(), 100));
@ -730,7 +773,7 @@ namespace IPC {
///\brief Creates the next page with the correct size
void sharedServer::newPage() {
sharedPage tmp(std::string(baseName.substr(1) + (char)(myPages.size() + (int)'A')), std::min(((8192 * 2)<< myPages.size()), (32 * 1024 * 1024)), true);
sharedPage tmp(std::string(baseName.substr(1) + (char)(myPages.size() + (int)'A')), std::min(((8192 * 2) << myPages.size()), (32 * 1024 * 1024)), true);
myPages.insert(tmp);
tmp.master = false;
DEBUG_MSG(DLVL_VERYHIGH, "Created a new page: %s", tmp.name.c_str());
@ -784,7 +827,7 @@ namespace IPC {
}
semGuard tmpGuard(&mySemaphore);
unsigned int id = 0;
unsigned int userCount=0;
unsigned int userCount = 0;
unsigned int emptyCount = 0;
for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) {
if (!it->mapped || !it->len) {
@ -796,16 +839,16 @@ namespace IPC {
while (offset + payLen + (hasCounter ? 1 : 0) <= it->len) {
if (hasCounter) {
if (it->mapped[offset] != 0) {
char * counter = it->mapped+offset;
char * counter = it->mapped + offset;
//increase the count if needed
++userCount;
if (id >= amount) {
amount = id + 1;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
}
unsigned short tmpPID = *((unsigned short *)(it->mapped+1+offset+payLen-2));
if(!Util::Procs::isRunning(tmpPID) && !(*counter == 126 || *counter == 127 || *counter == 254 || *counter == 255)){
WARN_MSG("process disappeared, timing out. (pid %d)", tmpPID);
}
unsigned short tmpPID = *((unsigned short *)(it->mapped + 1 + offset + payLen - 2));
if (!Util::Procs::isRunning(tmpPID) && !(*counter == 126 || *counter == 127 || *counter == 254 || *counter == 255)) {
WARN_MSG("process disappeared, timing out. (pid %d)", tmpPID);
*counter = 126; //if process is already dead, instant timeout.
}
callback(it->mapped + offset + 1, payLen, id);
@ -823,21 +866,21 @@ namespace IPC {
DEBUG_MSG(DLVL_WARN, "Client %u disconnect timed out", id);
break;
default:
#ifndef NOCRASHCHECK
if (tmpPID){
if(*counter > 10 && *counter < 126 ){
if(*counter < 30){
if (*counter > 15){
WARN_MSG("Process %d is unresponsive",tmpPID);
#ifndef NOCRASHCHECK
if (tmpPID) {
if (*counter > 10 && *counter < 126) {
if (*counter < 30) {
if (*counter > 15) {
WARN_MSG("Process %d is unresponsive", tmpPID);
}
Util::Procs::Stop(tmpPID); //soft kill
} else {
Util::Procs::Stop(tmpPID); //soft kill
} else {
ERROR_MSG("Killing unresponsive process %d", tmpPID);
Util::Procs::Murder(tmpPID); //improved kill
Util::Procs::Murder(tmpPID); //improved kill
}
}
}
#endif
#endif
break;
}
if (*counter == 127 || *counter == 126 || *counter == 255 || *counter == 254) {
@ -885,20 +928,20 @@ namespace IPC {
}
offset += payLen + (hasCounter ? 1 : 0);
id ++;
}
if(userCount==0) {
}
if (userCount == 0) {
++emptyCount;
} else {
emptyCount=0;
emptyCount = 0;
}
}
if( emptyCount > 1){
if (emptyCount > 1) {
deletePage();
} else if( !emptyCount ){
} else if (!emptyCount) {
newPage();
}
if (empty) {
free(empty);
}
@ -911,6 +954,7 @@ namespace IPC {
offsetOnPage = 0;
}
///\brief Copy constructor for sharedClients
///\param rhs The client ro copy
sharedClient::sharedClient(const sharedClient & rhs) {
@ -956,7 +1000,7 @@ namespace IPC {
///\param name The basename of the server to connect to
///\param len The size of the payload to allocate
///\param withCounter Whether or not this payload has a counter
sharedClient::sharedClient(std::string name, int len, bool withCounter) : baseName("/"+name), payLen(len), offsetOnPage(-1), hasCounter(withCounter) {
sharedClient::sharedClient(std::string name, int len, bool withCounter) : baseName("/" + name), payLen(len), offsetOnPage(-1), hasCounter(withCounter) {
#ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
@ -967,6 +1011,7 @@ namespace IPC {
DEBUG_MSG(DLVL_FAIL, "Creating semaphore %s failed: %s", baseName.c_str(), strerror(errno));
return;
}
//Empty is used to compare for emptyness. This is not needed when the page uses a counter
char * empty = 0;
if (!hasCounter) {
empty = (char *)malloc(payLen * sizeof(char));
@ -976,12 +1021,12 @@ namespace IPC {
}
memset(empty, 0, payLen);
}
while (offsetOnPage == -1){
while (offsetOnPage == -1) {
{
semGuard tmpGuard(&mySemaphore);
for (char i = 'A'; i <= 'Z'; i++) {
myPage.init(baseName.substr(1) + i, (4096 << (i - 'A')), false, false);
if (!myPage.mapped){
if (!myPage.mapped) {
break;
}
int offset = 0;
@ -990,7 +1035,7 @@ namespace IPC {
offsetOnPage = offset;
if (hasCounter) {
myPage.mapped[offset] = 1;
*((unsigned short *)(myPage.mapped+1+offset+len-2))=getpid();
*((unsigned short *)(myPage.mapped + 1 + offset + len - 2)) = getpid();
}
break;
}
@ -1001,11 +1046,13 @@ namespace IPC {
}
}
}
if (offsetOnPage == -1){
if (offsetOnPage == -1) {
Util::wait(500);
}
}
free(empty);
if (empty) {
free(empty);
}
}
///\brief The deconstructor
@ -1058,11 +1105,11 @@ namespace IPC {
}
userConnection::userConnection(char * _data) {
data = _data;
data = _data;
}
unsigned long userConnection::getTrackId(size_t offset) const {
if (offset >= SIMUL_TRACKS){
if (offset >= SIMUL_TRACKS) {
WARN_MSG("Trying to get track id for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
return 0;
}
@ -1070,16 +1117,16 @@ namespace IPC {
}
void userConnection::setTrackId(size_t offset, unsigned long trackId) const {
if (offset >= SIMUL_TRACKS){
if (offset >= SIMUL_TRACKS) {
WARN_MSG("Trying to set track id for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
return;
}
Bit::htobl(data + (offset * 6), trackId);
}
unsigned long userConnection::getKeynum(size_t offset) const {
if (offset >= SIMUL_TRACKS){
if (offset >= SIMUL_TRACKS) {
WARN_MSG("Trying to get keynum for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
return 0;
}
@ -1087,12 +1134,12 @@ namespace IPC {
}
void userConnection::setKeynum(size_t offset, unsigned long keynum) {
if (offset >= SIMUL_TRACKS){
if (offset >= SIMUL_TRACKS) {
WARN_MSG("Trying to set keynum for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
return;
}
Bit::htobs(data + (offset * 6) + 4, keynum);
}
}

View file

@ -69,6 +69,8 @@ namespace IPC {
void unlink();
private:
#if defined(__CYGWIN__) || defined(_WIN32)
///\todo Maybe sometime implement anything else than 777
static SECURITY_ATTRIBUTES getSecurityAttributes();
HANDLE mySem;
#else
sem_t * mySem;

View file

@ -690,6 +690,10 @@ namespace TS {
return data[0];
}
void ProgramMappingEntry::setStreamType(int newType){
data[0] = newType;
}
std::string ProgramMappingEntry::getCodec() const{
switch (getStreamType()){
case 0x01:
@ -740,10 +744,25 @@ namespace TS {
return ((data[1] << 8) | data[2]) & 0x1FFF;
}
void ProgramMappingEntry::setElementaryPid(int newElementaryPid) {
data[1] = newElementaryPid >> 8 & 0x1F;
data[2] = newElementaryPid & 0xFF;
}
int ProgramMappingEntry::getESInfoLength() const{
return ((data[3] << 8) | data[4]) & 0x0FFF;
}
const char * ProgramMappingEntry::getESInfo() const{
return data + 5;
}
void ProgramMappingEntry::setESInfo(const std::string & newInfo){
data[3] = (newInfo.size() >> 8) & 0x0F;
data[4] = newInfo.size() & 0xFF;
memcpy(data + 5, newInfo.data(), newInfo.size());
}
void ProgramMappingEntry::advance(){
if (!(*this)) {
return;
@ -884,14 +903,6 @@ namespace TS {
strBuf[loc+1] = (char)newVal;
}
short ProgramMappingTable::getProgramCount() const{
return (getSectionLength() - 13) / 5;
}
void ProgramMappingTable::setProgramCount(short newVal) {
setSectionLength(newVal * 5 + 13);
}
ProgramMappingEntry ProgramMappingTable::getEntry(int index) const{
int dataOffset = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset();
ProgramMappingEntry res((char*)(strBuf + dataOffset + 13 + getProgramInfoLength()), (char*)(strBuf + dataOffset + getSectionLength()) );
@ -901,59 +912,6 @@ namespace TS {
return res;
}
char ProgramMappingTable::getStreamType(short index) const{
if (index > getProgramCount()) {
return 0;
}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength();
return strBuf[loc + (index * 5)];
}
void ProgramMappingTable::setStreamType(char newVal, short index) {
if (index > getProgramCount()) {
return;
}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength(); //TODO
updPos(loc+(index*5)+1);
strBuf[loc + (index * 5)] = newVal;
}
short ProgramMappingTable::getElementaryPID(short index) const{
if (index > getProgramCount()) {
return 0;
}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength();
return (((short)strBuf[loc + (index * 5) + 1] & 0x1F) << 8) | strBuf[loc + (index * 5) + 2];
}
void ProgramMappingTable::setElementaryPID(short newVal, short index) {
if (index > getProgramCount()) {
return;
}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength();
updPos(loc+(index*5)+3);
strBuf[loc + (index * 5)+1] = ((newVal >> 8) & 0x1F )| 0xE0;
strBuf[loc + (index * 5)+2] = (char)newVal;
}
short ProgramMappingTable::getESInfoLength(short index) const{
if (index > getProgramCount()) {
return 0;
}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength();
return (((short)strBuf[loc + (index * 5) + 3] & 0x0F) << 8) | strBuf[loc + (index * 5) + 4];
}
void ProgramMappingTable::setESInfoLength(short newVal, short index) {
if (index > getProgramCount()) {
return;
}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength();
updPos(loc+(index*5)+5);
strBuf[loc + (index * 5)+3] = ((newVal >> 8) & 0x0F) | 0xF0;
strBuf[loc + (index * 5)+4] = (char)newVal;
}
int ProgramMappingTable::getCRC() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + getSectionLength();
return ((int)(strBuf[loc]) << 24) | ((int)(strBuf[loc + 1]) << 16) | ((int)(strBuf[loc + 2]) << 8) | strBuf[loc + 3];
@ -1011,7 +969,14 @@ namespace TS {
PMT.setPID(4096);
PMT.setTableId(2);
//section length met 2 tracks: 0xB017
PMT.setSectionLength(0xB00D + (selectedTracks.size() * 5));
int sectionLen = 0;
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
sectionLen += 5;
if (myMeta.tracks[*it].codec == "ID3"){
sectionLen += myMeta.tracks[*it].init.size();
}
}
PMT.setSectionLength(0xB00D + sectionLen);
PMT.setProgramNumber(1);
PMT.setVersionNumber(0);
PMT.setCurrentNextIndicator(0);
@ -1028,24 +993,27 @@ namespace TS {
if (vidTrack == -1){
vidTrack = *(selectedTracks.begin());
}
PMT.setPCRPID(0x100 + vidTrack - 1);
PMT.setPCRPID(vidTrack);
PMT.setProgramInfoLength(0);
short id = 0;
ProgramMappingEntry entry = PMT.getEntry(0);
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
entry.setElementaryPid(*it);
if (myMeta.tracks[*it].codec == "H264"){
PMT.setStreamType(0x1B,id);
entry.setStreamType(0x1B);
}else if (myMeta.tracks[*it].codec == "HEVC"){
PMT.setStreamType(0x24,id);
entry.setStreamType(0x24);
}else if (myMeta.tracks[*it].codec == "AAC"){
PMT.setStreamType(0x0F,id);
entry.setStreamType(0x0F);
}else if (myMeta.tracks[*it].codec == "MP3"){
PMT.setStreamType(0x03,id);
entry.setStreamType(0x03);
}else if (myMeta.tracks[*it].codec == "AC3"){
PMT.setStreamType(0x81,id);
entry.setStreamType(0x81);
}else if (myMeta.tracks[*it].codec == "ID3"){
entry.setStreamType(0x15);
entry.setESInfo(myMeta.tracks[*it].init);
}
PMT.setElementaryPID(0x100 + (*it) - 1, id);
PMT.setESInfoLength(0,id);
id++;
entry.advance();
}
PMT.calcCRC();
return PMT.checkAndGetBuffer();

View file

@ -107,11 +107,14 @@ namespace TS {
operator bool() const;
int getStreamType() const;
void setStreamType(int newType);
std::string getCodec() const;
std::string getStreamTypeString() const;
int getElementaryPid() const;
void setElementaryPid(int newElementaryPid);
int getESInfoLength() const;
char * getESInfo() const;
const char * getESInfo() const;
void setESInfo(const std::string & newInfo);
void advance();
private:
char* data;
@ -142,15 +145,7 @@ namespace TS {
void setPCRPID(short newVal);
short getProgramInfoLength() const;
void setProgramInfoLength(short newVal);
short getProgramCount() const;
void setProgramCount(short newVal);
ProgramMappingEntry getEntry(int index) const;
void setStreamType(char newVal, short index);
char getStreamType(short index) const;
void setElementaryPID(short newVal, short index);
short getElementaryPID(short index) const;
void setESInfoLength(short newVal,short index);
short getESInfoLength(short index) const;
int getCRC() const;
void calcCRC();
std::string toPrettyString(size_t indent) const;

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

View file

@ -1,9 +1,12 @@
#include "ts_packet.h"
#include "adts.h"
#include <map>
#include <set>
#include <deque>
#include "h265.h"
#include "shared_memory.h"
namespace TS {
enum codecType {
H264 = 0x1B,
@ -11,32 +14,47 @@ namespace TS {
AC3 = 0x81,
MP3 = 0x03,
HEVC = 0x06,
H265 = 0x24
H265 = 0x24,
ID3 = 0x15
};
class Stream{
public:
Stream(bool _threaded = false);
void add(char * newPack, unsigned long long bytePos = 0);
void add(Packet & newPack, unsigned long long bytePos = 0);
void parse(Packet & newPack, unsigned long long bytePos);
void parse(char * newPack, unsigned long long bytePos);
void parse(unsigned long tid);
bool hasPacketOnEachTrack() const;
bool hasPacket(unsigned long tid) const;
void getPacket(unsigned long tid, DTSC::Packet & pack);
void getEarliestPacket(DTSC::Packet & pack);
void initializeMetadata(DTSC::Meta & meta);
void initializeMetadata(DTSC::Meta & meta, unsigned long tid = 0);
void clear();
void eraseTrack(unsigned long tid);
bool isDataTrack(unsigned long tid);
std::set<unsigned long> getActiveTracks();
private:
unsigned long long lastPAT;
ProgramAssociationTable associationTable;
std::map<unsigned long, unsigned long long> lastPMT;
std::map<unsigned long, ProgramMappingTable> mappingTable;
std::map<unsigned long, std::deque<Packet> > pesStreams;
std::map<unsigned long, std::deque<unsigned long long> > pesPositions;
std::map<unsigned long, unsigned long> payloadSize;
std::map<unsigned long, std::deque<DTSC::Packet> > outPackets;
std::map<unsigned long, unsigned long> pidToCodec;
std::map<unsigned long, aac::adts > adtsInfo;
std::map<unsigned long, std::string > spsInfo;
std::map<unsigned long, std::string > ppsInfo;
std::map<unsigned long, h265::initData > hevcInfo;
std::map<unsigned long, std::string> metaInit;
mutable IPC::semaphore globalSem;
bool threaded;
std::set<unsigned long> pmtTracks;