New Meta commit

This commit is contained in:
Phencys 2021-04-21 18:10:03 +02:00 committed by Thulinma
parent fccf66fba2
commit 2b99f2f5ea
183 changed files with 13333 additions and 14421 deletions

View file

@ -1,3 +1,4 @@
#include "bitfields.h"
#include "bitstream.h"
#include "defines.h"
#include <stdlib.h>
@ -142,62 +143,40 @@ namespace Utils{
long long unsigned int bitstream::peekUExpGolomb(){return golombPeeker() - 1;}
bitWriter::bitWriter(){
dataBuffer = NULL;
bufferSize = 0;
reallocate(0);
dataSize = 0;
bitWriter::bitWriter(){bitSize = 0;}
size_t bitWriter::size() const{return bitSize;}
void bitWriter::append(const std::string &val){
for (size_t i = 0; i < val.size(); i++){append(val[i]);}
}
bitWriter::~bitWriter(){
if (dataBuffer != NULL){free(dataBuffer);}
}
void bitWriter::append(uint64_t val, size_t bitLength){
static char buf[9];
void bitWriter::reallocate(size_t newSize){
size_t sizeBefore = bufferSize / 8;
char *tmp;
if (dataBuffer != NULL){
tmp = (char *)realloc(dataBuffer, (newSize / 8) + 1);
uint32_t byteLength = ((bitSize + bitLength) / 8) + 1;
while (byteLength > p.size()){p.append("", 1);}
int bitShift = (64 - bitLength) - (bitSize % 8);
if (bitShift >= 0){
Bit::htobll(buf, val << bitShift);
}else{
tmp = (char *)malloc((newSize / 8) + 1);
}
if (tmp){
dataBuffer = tmp;
bufferSize = ((newSize / 8) + 1) * 8;
memset(dataBuffer + sizeBefore, 0x00, (bufferSize / 8) - sizeBefore);
}else{
FAIL_MSG("Could not reallocate!!");
Bit::htobll(buf, val >> (bitShift * -1));
buf[8] = ((val << (8 + bitShift)) & 0xFF);
}
size_t adjustableBits = (bitSize % 8) + bitLength;
size_t adjustableBytes = adjustableBits / 8 + (adjustableBits % 8 ? 1 : 0);
for (int i = 0; i < adjustableBytes; i++){p[bitSize / 8 + i] |= buf[i];}
bitSize += bitLength;
}
size_t bitWriter::size(){return dataSize;}
void bitWriter::append(uint64_t value, size_t bitLength){
if (dataSize + bitLength > bufferSize){reallocate(dataSize + bitLength);}
int64_t fullShift = (bitLength / 8) * 8;
uint64_t firstMask = ((0x01ull << (bitLength % 8)) - 1) << fullShift;
appendData(((value & firstMask) >> fullShift), bitLength - fullShift);
while (fullShift > 0){
fullShift -= 8;
uint64_t mask = (0xFFull) << fullShift;
appendData((value & mask) >> fullShift, 8);
}
}
void bitWriter::appendData(uint8_t data, size_t len){
size_t byteOffset = dataSize / 8;
size_t bitOffset = dataSize % 8;
if (len <= 8 - bitOffset){
dataBuffer[byteOffset] |= (data << (8 - bitOffset - len));
dataSize += len;
}else{
size_t shift = (len - (8 - bitOffset));
dataBuffer[byteOffset] |= (data >> shift);
dataSize += (len - shift);
appendData(data, shift);
}
void bitWriter::clear(){
p.assign("", 0);
bitSize = 0;
}
size_t bitWriter::UExpGolombEncodedSize(uint64_t value){

View file

@ -1,5 +1,6 @@
#pragma once
#include "defines.h"
#include "util.h"
#include <string>
namespace Utils{
@ -52,23 +53,20 @@ namespace Utils{
class bitWriter{
public:
bitWriter();
~bitWriter();
size_t size();
void append(uint64_t value, size_t bitLength);
size_t size() const;
void append(const std::string &val);
void append(uint64_t val, size_t bitLength = 8);
void appendExpGolomb(int64_t value);
void appendUExpGolomb(uint64_t value);
static size_t UExpGolombEncodedSize(uint64_t value);
std::string str(){return std::string(dataBuffer, (dataSize / 8) + (dataSize % 8 ? 1 : 0));}
std::string str(){return std::string(p, (bitSize / 8) + (bitSize % 8 ? 1 : 0));}
void clear();
protected:
void reallocate(size_t newSize);
void appendData(uint8_t data, size_t len);
char *dataBuffer;
// NOTE: ALL SIZES IN BITS!
size_t bufferSize;
size_t dataSize;
// void appendData(uint8_t data, size_t len);
size_t bitSize;
Util::ResizeablePointer p;
};
class bitstreamLSBF{

View file

@ -213,7 +213,6 @@ std::string Certificate::getFingerprintSha256(){
uint8_t fingerprint_raw[32] ={};
uint8_t fingerprint_hex[128] ={};
mbedtls_md_type_t hash_type = MBEDTLS_MD_SHA256;
mbedtls_sha256(cert.raw.p, cert.raw.len, fingerprint_raw, 0);

302
lib/cmaf.cpp Normal file
View file

@ -0,0 +1,302 @@
#include "cmaf.h"
namespace CMAF{
size_t payloadSize(const DTSC::Meta &M, size_t track, size_t fragment){
DTSC::Fragments fragments(M.fragments(track));
DTSC::Keys keys(M.keys(track));
DTSC::Parts parts(M.parts(track));
size_t firstKey = fragments.getFirstKey(fragment);
size_t endKey = keys.getEndValid();
if (fragment + 1 < fragments.getEndValid()){endKey = fragments.getFirstKey(fragment + 1);}
size_t firstPart = keys.getFirstPart(firstKey);
size_t endPart = parts.getEndValid();
if (endKey != keys.getEndValid()){endPart = keys.getFirstPart(endKey);}
size_t payloadSize = 0;
for (size_t i = firstPart; i < endPart; i++){payloadSize += parts.getSize(i);}
return payloadSize;
}
size_t trackHeaderSize(const DTSC::Meta &M, size_t track){
// EDTS Box needed? + 36
size_t res = 36 + 8 + 108 + 8 + 92 + 8 + 32 + 33 + 44 + 8 + 20 + 16 + 16 + 16 + 40;
res += M.getTrackIdentifier(track).size();
// Type-specific boxes
std::string tType = M.getType(track);
if (tType == "video"){res += 20 + 16 + 86 + 16 + 8 + M.getInit(track).size();}
if (tType == "audio"){
res += 16 + 16 + 36 + 35 + (M.getInit(track).size() ? 2 + M.getInit(track).size() : 0);
}
if (tType == "meta"){res += 12 + 16 + 64;}
if (M.getVod()){res += 16;}
return res;
}
std::string trackHeader(const DTSC::Meta &M, size_t track){
std::string tType = M.getType(track);
std::stringstream header;
MP4::FTYP ftypBox;
ftypBox.setMajorBrand("isom");
ftypBox.setCompatibleBrands("cmfc", 0);
ftypBox.setCompatibleBrands("isom", 1);
ftypBox.setCompatibleBrands("dash", 2);
ftypBox.setCompatibleBrands("iso9", 3);
header.write(ftypBox.asBox(), ftypBox.boxedSize());
MP4::MOOV moovBox;
MP4::MVHD mvhdBox(0);
mvhdBox.setTrackID(track + 2); // This value needs to point to an unused trackid
moovBox.setContent(mvhdBox, 0);
MP4::TRAK trakBox;
MP4::TKHD tkhdBox(M, track);
tkhdBox.setDuration(0);
trakBox.setContent(tkhdBox, 0);
MP4::MDIA mdiaBox;
MP4::MDHD mdhdBox(0, M.getLang(track));
mdiaBox.setContent(mdhdBox, 0);
MP4::HDLR hdlrBox(tType, M.getTrackIdentifier(track));
mdiaBox.setContent(hdlrBox, 1);
MP4::MINF minfBox;
if (tType == "video"){
MP4::VMHD vmhdBox;
vmhdBox.setFlags(1);
minfBox.setContent(vmhdBox, 0);
}else if (tType == "audio"){
MP4::SMHD smhdBox;
minfBox.setContent(smhdBox, 0);
}else{
MP4::NMHD nmhdBox;
minfBox.setContent(nmhdBox, 0);
}
MP4::DINF dinfBox;
MP4::DREF drefBox;
dinfBox.setContent(drefBox, 0);
minfBox.setContent(dinfBox, 1);
MP4::STBL stblBox;
// Add STSD box
MP4::STSD stsdBox(0);
if (tType == "video"){
MP4::VisualSampleEntry sampleEntry(M, track);
stsdBox.setEntry(sampleEntry, 0);
}else if (tType == "audio"){
MP4::AudioSampleEntry sampleEntry(M, track);
stsdBox.setEntry(sampleEntry, 0);
}else if (tType == "meta"){
MP4::TextSampleEntry sampleEntry(M, track);
MP4::FontTableBox ftab;
sampleEntry.setFontTableBox(ftab);
stsdBox.setEntry(sampleEntry, 0);
}
stblBox.setContent(stsdBox, 0);
MP4::STTS sttsBox(0);
stblBox.setContent(sttsBox, 1);
MP4::STSC stscBox(0);
stblBox.setContent(stscBox, 2);
MP4::STSZ stszBox(0);
stblBox.setContent(stszBox, 3);
MP4::STCO stcoBox(0);
stblBox.setContent(stcoBox, 4);
minfBox.setContent(stblBox, 2);
mdiaBox.setContent(minfBox, 2);
trakBox.setContent(mdiaBox, 1);
moovBox.setContent(trakBox, 1);
MP4::MVEX mvexBox;
if (M.getVod()){
MP4::MEHD mehdBox;
mehdBox.setFragmentDuration(M.getDuration(track));
mvexBox.setContent(mehdBox, 0);
}
MP4::TREX trexBox(track + 1);
trexBox.setDefaultSampleDuration(1000);
mvexBox.setContent(trexBox, M.getVod() ? 1 : 0);
moovBox.setContent(mvexBox, 2);
header.write(moovBox.asBox(), moovBox.boxedSize());
if (M.getVod()){
DTSC::Fragments fragments(M.fragments(track));
DTSC::Keys keys(M.keys(track));
DTSC::Parts parts(M.parts(track));
MP4::SIDX sidxBox;
sidxBox.setReferenceID(track + 1);
sidxBox.setTimescale(1000);
sidxBox.setEarliestPresentationTime(keys.getTime(0) + parts.getOffset(0) - M.getFirstms(track));
for (size_t i = 0; i < fragments.getEndValid(); i++){
size_t firstKey = fragments.getFirstKey(i);
size_t endKey =
((i + 1 < fragments.getEndValid()) ? fragments.getFirstKey(i + 1) : keys.getEndValid());
MP4::sidxReference refItem;
refItem.referencedSize = payloadSize(M, track, i) + fragmentHeaderSize(M, track, i) + 8;
refItem.subSegmentDuration =
(endKey == keys.getEndValid() ? M.getLastms(track) : keys.getTime(endKey)) - keys.getTime(firstKey);
refItem.sapStart = true;
refItem.sapType = 16;
refItem.sapDeltaTime = 0;
refItem.referenceType = 0;
sidxBox.setReference(refItem, i);
}
header.write(sidxBox.asBox(), sidxBox.boxedSize());
}
return header.str();
}
class sortPart{
public:
uint64_t time;
size_t partIndex;
size_t bytePos;
bool operator<(const sortPart &rhs) const{return time < rhs.time;}
};
size_t fragmentHeaderSize(const DTSC::Meta &M, size_t track, size_t fragment){
uint64_t tmpRes = 8 + 16 + 32 + 20;
DTSC::Fragments fragments(M.fragments(track));
DTSC::Keys keys(M.keys(track));
DTSC::Parts parts(M.parts(track));
size_t firstKey = fragments.getFirstKey(fragment);
size_t firstPart = keys.getFirstPart(firstKey);
size_t endPart = parts.getEndValid();
if (fragment + 1 < fragments.getEndValid()){
endPart = keys.getFirstPart(fragments.getFirstKey(fragment + 1));
}
tmpRes += 24 + ((endPart - firstPart) * 12);
return tmpRes;
}
std::string fragmentHeader(const DTSC::Meta &M, size_t track, size_t fragment){
DTSC::Fragments fragments(M.fragments(track));
DTSC::Keys keys(M.keys(track));
DTSC::Parts parts(M.parts(track));
size_t firstKey = fragments.getFirstKey(fragment);
size_t endKey = keys.getEndValid();
if (fragment + 1 < fragments.getEndValid()){endKey = fragments.getFirstKey(fragment + 1);}
std::stringstream header;
if (M.getLive()){
MP4::SIDX sidxBox;
sidxBox.setTimescale(1000);
sidxBox.setEarliestPresentationTime(keys.getTime(firstKey));
MP4::sidxReference refItem;
refItem.referencedSize = 230000;
refItem.subSegmentDuration = keys.getTime(endKey) - keys.getTime(firstKey);
refItem.sapStart = true;
refItem.sapType = 16;
refItem.sapDeltaTime = 0;
refItem.referenceType = 0;
sidxBox.setReference(refItem, 0);
sidxBox.setReferenceID(1);
header.write(sidxBox.asBox(), sidxBox.boxedSize());
}
MP4::MOOF moofBox;
MP4::MFHD mfhdBox(fragment + 1);
moofBox.setContent(mfhdBox, 0);
size_t firstPart = keys.getFirstPart(firstKey);
size_t endPart = parts.getEndValid();
if (fragment + 1 < fragments.getEndValid()){
endPart = keys.getFirstPart(fragments.getFirstKey(fragment + 1));
}
std::set<sortPart> trunOrder;
uint64_t relativeOffset = fragmentHeaderSize(M, track, fragment) + 8;
sortPart temp;
temp.time = keys.getTime(firstKey);
temp.partIndex = keys.getFirstPart(firstKey);
temp.bytePos = relativeOffset;
for (size_t p = firstPart; p < endPart; p++){
trunOrder.insert(temp);
temp.time += parts.getDuration(p);
temp.partIndex++;
temp.bytePos += parts.getSize(p);
}
MP4::TRAF trafBox;
MP4::TFHD tfhdBox;
tfhdBox.setFlags(MP4::tfhdSampleFlag | MP4::tfhdBaseIsMoof | MP4::tfhdSampleDesc);
tfhdBox.setTrackID(track + 1);
tfhdBox.setDefaultSampleDuration(444);
tfhdBox.setDefaultSampleSize(444);
tfhdBox.setDefaultSampleFlags((M.getType(track) == "video") ? (MP4::noIPicture | MP4::noKeySample)
: (MP4::isIPicture | MP4::isKeySample));
tfhdBox.setSampleDescriptionIndex(0);
trafBox.setContent(tfhdBox, 0);
MP4::TFDT tfdtBox;
if (M.getVod()){
tfdtBox.setBaseMediaDecodeTime(M.getTimeForFragmentIndex(track, fragment) - M.getFirstms(track));
}else{
tfdtBox.setBaseMediaDecodeTime(M.getTimeForFragmentIndex(track, fragment));
}
trafBox.setContent(tfdtBox, 1);
MP4::TRUN trunBox;
trunBox.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleSize |
MP4::trunsampleDuration | MP4::trunsampleOffsets);
// The value set here, will be updated afterwards to the correct value
trunBox.setDataOffset(trunOrder.begin()->bytePos);
trunBox.setFirstSampleFlags(MP4::isIPicture | MP4::isKeySample);
size_t trunOffset = 0;
for (std::set<sortPart>::iterator it = trunOrder.begin(); it != trunOrder.end(); it++){
MP4::trunSampleInformation sampleInfo;
sampleInfo.sampleSize = parts.getSize(it->partIndex);
sampleInfo.sampleDuration = parts.getDuration(it->partIndex);
sampleInfo.sampleOffset = parts.getOffset(it->partIndex);
trunBox.setSampleInformation(sampleInfo, trunOffset++);
}
trafBox.setContent(trunBox, 2);
moofBox.setContent(trafBox, 1);
header.write(moofBox.asBox(), moofBox.boxedSize());
return header.str();
}
}// namespace CMAF

12
lib/cmaf.h Normal file
View file

@ -0,0 +1,12 @@
#include "dtsc.h"
#include "mp4_dash.h"
#include "mp4_generic.h"
#include <set>
namespace CMAF{
size_t payloadSize(const DTSC::Meta &M, size_t track, size_t fragment);
size_t trackHeaderSize(const DTSC::Meta &M, size_t track);
std::string trackHeader(const DTSC::Meta &M, size_t track);
size_t fragmentHeaderSize(const DTSC::Meta &M, size_t track, size_t fragment);
std::string fragmentHeader(const DTSC::Meta &M, size_t track, size_t fragment);
}// namespace CMAF

440
lib/comms.cpp Normal file
View file

@ -0,0 +1,440 @@
#include "auth.h"
#include "bitfields.h"
#include "comms.h"
#include "defines.h"
#include "encode.h"
#include "procs.h"
#include "timing.h"
namespace Comms{
Comms::Comms(){
index = INVALID_RECORD_INDEX;
currentSize = 0;
master = false;
}
Comms::~Comms(){
if (index != INVALID_RECORD_INDEX){setStatus(COMM_STATUS_DISCONNECT);}
if (master){
if (dataPage.mapped){
finishAll();
dataPage.master = true;
}
sem.unlink();
}
sem.close();
}
void Comms::addCommonFields(){
dataAccX.addField("status", RAX_UINT);
dataAccX.addField("command", RAX_64UINT);
dataAccX.addField("timer", RAX_UINT);
dataAccX.addField("pid", RAX_32UINT);
dataAccX.addField("killtime", RAX_64UINT);
}
void Comms::commonFieldAccess(){
status = dataAccX.getFieldAccX("status");
command = dataAccX.getFieldAccX("command");
timer = dataAccX.getFieldAccX("timer");
pid = dataAccX.getFieldAccX("pid");
killTime = dataAccX.getFieldAccX("killtime");
}
size_t Comms::firstValid() const{
if (!master){return index;}
return dataAccX.getStartPos();
}
size_t Comms::endValid() const{
if (!master){return index + 1;}
return dataAccX.getEndPos();
}
void Comms::deleteFirst(){
if (!master){return;}
dataAccX.deleteRecords(1);
}
uint8_t Comms::getStatus() const{return status.uint(index);}
uint8_t Comms::getStatus(size_t idx) const{return (master ? status.uint(idx) : 0);}
void Comms::setStatus(uint8_t _status){status.set(_status, index);}
void Comms::setStatus(uint8_t _status, size_t idx){
if (!master){return;}
status.set(_status, idx);
}
uint64_t Comms::getCommand() const{return command.uint(index);}
uint64_t Comms::getCommand(size_t idx) const{return (master ? command.uint(idx) : 0);}
void Comms::setCommand(uint64_t _cmd){command.set(_cmd, index);}
void Comms::setCommand(uint64_t _cmd, size_t idx){
if (!master){return;}
command.set(_cmd, idx);
}
uint8_t Comms::getTimer() const{return timer.uint(index);}
uint8_t Comms::getTimer(size_t idx) const{return (master ? timer.uint(idx) : 0);}
void Comms::setTimer(uint8_t _timer){timer.set(_timer, index);}
void Comms::setTimer(uint8_t _timer, size_t idx){
if (!master){return;}
timer.set(_timer, idx);
}
uint32_t Comms::getPid() const{return pid.uint(index);}
uint32_t Comms::getPid(size_t idx) const{return (master ? pid.uint(idx) : 0);}
void Comms::setPid(uint32_t _pid){pid.set(_pid, index);}
void Comms::setPid(uint32_t _pid, size_t idx){
if (!master){return;}
pid.set(_pid, idx);
}
void Comms::kill(size_t idx, bool force){
if (!master){return;}
if (force){
Util::Procs::Murder(pid.uint(idx)); // hard kill
status.set(COMM_STATUS_INVALID, idx);
return;
}
uint64_t kTime = killTime.uint(idx);
uint64_t now = Util::bootSecs();
if (!kTime){
kTime = now;
killTime.set(kTime, idx);
}
if (now - kTime > 30){
Util::Procs::Murder(pid.uint(idx)); // hard kill
status.set(COMM_STATUS_INVALID, idx);
}else{
Util::Procs::Stop(pid.uint(idx)); // soft kill
}
}
void Comms::finishAll(){
if (!master){return;}
size_t c = 0;
do{
for (size_t i = firstValid(); i < endValid(); i++){
if (getStatus(i) == COMM_STATUS_INVALID){continue;}
setStatus(COMM_STATUS_DISCONNECT, i);
}
while (getStatus(firstValid()) == COMM_STATUS_INVALID){deleteFirst();}
}while (firstValid() < endValid() && ++c < 10);
}
void Comms::keepAlive(){
if (isAlive()){setTimer(0);}
}
bool Comms::isAlive() const{
if (!*this){return false;}
if (getStatus() == COMM_STATUS_INVALID){return false;}
if (getStatus() == COMM_STATUS_DISCONNECT){return false;}
return getTimer() < 126;
}
void Comms::setMaster(bool _master){
master = _master;
dataPage.master = _master;
}
Statistics::Statistics() : Comms(){sem.open(SEM_STATISTICS, O_CREAT | O_RDWR, ACCESSPERMS, 1);}
void Statistics::unload(){
if (index != INVALID_RECORD_INDEX){setStatus(COMM_STATUS_DISCONNECT);}
index = INVALID_RECORD_INDEX;
}
void Statistics::reload(bool _master, bool reIssue){
master = _master;
bool setFields = true;
if (!currentSize){currentSize = COMMS_STATISTICS_INITSIZE;}
dataPage.init(COMMS_STATISTICS, currentSize, false, false);
if (master){
if (dataPage.mapped){
setFields = false;
dataPage.master = true;
}else{
dataPage.init(COMMS_STATISTICS, currentSize, true);
}
}
if (!dataPage.mapped){
FAIL_MSG("Unable to open page " COMMS_STATISTICS);
return;
}
if (master){
dataAccX = Util::RelAccX(dataPage.mapped, false);
if (setFields){
addCommonFields();
dataAccX.addField("sync", RAX_UINT);
dataAccX.addField("now", RAX_64UINT);
dataAccX.addField("time", RAX_64UINT);
dataAccX.addField("lastsecond", RAX_64UINT);
dataAccX.addField("down", RAX_64UINT);
dataAccX.addField("up", RAX_64UINT);
dataAccX.addField("host", RAX_STRING, 16);
dataAccX.addField("stream", RAX_STRING, 100);
dataAccX.addField("connector", RAX_STRING, 20);
dataAccX.addField("crc", RAX_32UINT);
dataAccX.setRCount((currentSize - dataAccX.getOffset()) / dataAccX.getRSize());
dataAccX.setReady();
}
}else{
dataAccX = Util::RelAccX(dataPage.mapped);
if (index == INVALID_RECORD_INDEX || reIssue){
sem.wait();
for (index = 0; index < dataAccX.getEndPos(); ++index){
if (dataAccX.getInt("status", index) == COMM_STATUS_INVALID){
// Reverse! clear entry and claim it.
dataAccX.setInt("crc", 0, index);
dataAccX.setString("connector", "", index);
dataAccX.setString("stream", "", index);
dataAccX.setString("host", "", index);
dataAccX.setInt("up", 0, index);
dataAccX.setInt("down", 0, index);
dataAccX.setInt("lastsecond", 0, index);
dataAccX.setInt("time", 0, index);
dataAccX.setInt("now", 0, index);
dataAccX.setInt("sync", 0, index);
dataAccX.setInt("killtime", 0, index);
dataAccX.setInt("pid", 0, index);
dataAccX.setInt("timer", 0, index);
dataAccX.setInt("command", 0, index);
dataAccX.setInt("status", 0, index);
break;
}
}
if (index == dataAccX.getEndPos()){dataAccX.addRecords(1);}
sem.post();
}
}
commonFieldAccess();
sync = dataAccX.getFieldAccX("sync");
now = dataAccX.getFieldAccX("now");
time = dataAccX.getFieldAccX("time");
lastSecond = dataAccX.getFieldAccX("lastsecond");
down = dataAccX.getFieldAccX("down");
up = dataAccX.getFieldAccX("up");
host = dataAccX.getFieldAccX("host");
stream = dataAccX.getFieldAccX("stream");
connector = dataAccX.getFieldAccX("connector");
crc = dataAccX.getFieldAccX("crc");
}
uint8_t Statistics::getSync() const{return sync.uint(index);}
uint8_t Statistics::getSync(size_t idx) const{return (master ? sync.uint(idx) : 0);}
void Statistics::setSync(uint8_t _sync){sync.set(_sync, index);}
void Statistics::setSync(uint8_t _sync, size_t idx){
if (!master){return;}
sync.set(_sync, idx);
}
uint64_t Statistics::getNow() const{return now.uint(index);}
uint64_t Statistics::getNow(size_t idx) const{return (master ? now.uint(idx) : 0);}
void Statistics::setNow(uint64_t _now){now.set(_now, index);}
void Statistics::setNow(uint64_t _now, size_t idx){
if (!master){return;}
now.set(_now, idx);
}
uint64_t Statistics::getTime() const{return time.uint(index);}
uint64_t Statistics::getTime(size_t idx) const{return (master ? time.uint(idx) : 0);}
void Statistics::setTime(uint64_t _time){time.set(_time, index);}
void Statistics::setTime(uint64_t _time, size_t idx){
if (!master){return;}
time.set(_time, idx);
}
uint64_t Statistics::getLastSecond() const{return lastSecond.uint(index);}
uint64_t Statistics::getLastSecond(size_t idx) const{
return (master ? lastSecond.uint(idx) : 0);
}
void Statistics::setLastSecond(uint64_t _lastSecond){lastSecond.set(_lastSecond, index);}
void Statistics::setLastSecond(uint64_t _lastSecond, size_t idx){
if (!master){return;}
lastSecond.set(_lastSecond, idx);
}
uint64_t Statistics::getDown() const{return down.uint(index);}
uint64_t Statistics::getDown(size_t idx) const{return (master ? down.uint(idx) : 0);}
void Statistics::setDown(uint64_t _down){down.set(_down, index);}
void Statistics::setDown(uint64_t _down, size_t idx){
if (!master){return;}
down.set(_down, idx);
}
uint64_t Statistics::getUp() const{return up.uint(index);}
uint64_t Statistics::getUp(size_t idx) const{return (master ? up.uint(idx) : 0);}
void Statistics::setUp(uint64_t _up){up.set(_up, index);}
void Statistics::setUp(uint64_t _up, size_t idx){
if (!master){return;}
up.set(_up, idx);
}
std::string Statistics::getHost() const{return host.string(index);}
std::string Statistics::getHost(size_t idx) const{return (master ? host.string(idx) : "");}
void Statistics::setHost(std::string _host){host.set(_host, index);}
void Statistics::setHost(std::string _host, size_t idx){
if (!master){return;}
host.set(_host, idx);
}
std::string Statistics::getStream() const{return stream.string(index);}
std::string Statistics::getStream(size_t idx) const{return (master ? stream.string(idx) : "");}
void Statistics::setStream(std::string _stream){stream.set(_stream, index);}
void Statistics::setStream(std::string _stream, size_t idx){
if (!master){return;}
stream.set(_stream, idx);
}
std::string Statistics::getConnector() const{return connector.string(index);}
std::string Statistics::getConnector(size_t idx) const{
return (master ? connector.string(idx) : "");
}
void Statistics::setConnector(std::string _connector){connector.set(_connector, index);}
void Statistics::setConnector(std::string _connector, size_t idx){
if (!master){return;}
connector.set(_connector, idx);
}
uint32_t Statistics::getCRC() const{return crc.uint(index);}
uint32_t Statistics::getCRC(size_t idx) const{return (master ? crc.uint(idx) : 0);}
void Statistics::setCRC(uint32_t _crc){crc.set(_crc, index);}
void Statistics::setCRC(uint32_t _crc, size_t idx){
if (!master){return;}
crc.set(_crc, idx);
}
std::string Statistics::getSessId() const{return getSessId(index);}
std::string Statistics::getSessId(size_t idx) const{
char res[140];
memset(res, 0, 140);
std::string tmp = host.string(idx);
memcpy(res, tmp.c_str(), (tmp.size() > 16 ? 16 : tmp.size()));
tmp = stream.string(idx);
memcpy(res + 16, tmp.c_str(), (tmp.size() > 100 ? 100 : tmp.size()));
tmp = connector.string(idx);
memcpy(res + 116, tmp.c_str(), (tmp.size() > 20 ? 20 : tmp.size()));
Bit::htobl(res + 136, crc.uint(idx));
return Secure::md5(res, 140);
}
Users::Users() : Comms(){}
Users::Users(const Users &rhs) : Comms(){
if (rhs && rhs.isAlive()){
reload(rhs.streamName, (size_t)rhs.getTrack());
if (*this){
setKeyNum(rhs.getKeyNum());
setTrack(rhs.getTrack());
}
}
}
void Users::reload(const std::string &_streamName, bool _master, bool reIssue){
streamName = _streamName;
char semName[NAME_BUFFER_SIZE];
snprintf(semName, NAME_BUFFER_SIZE, SEM_USERS, streamName.c_str());
sem.open(semName, O_CREAT | O_RDWR, ACCESSPERMS, 1);
master = _master;
if (!currentSize){currentSize = COMMS_USERS_INITSIZE;}
char userPageName[NAME_BUFFER_SIZE];
snprintf(userPageName, NAME_BUFFER_SIZE, COMMS_USERS, streamName.c_str());
bool newPage = false;
if (master){
dataPage.init(userPageName, currentSize, false, false);
if (dataPage){
dataPage.master = true;
}else{
dataPage.init(userPageName, currentSize, true);
newPage = true;
}
}else{
dataPage.init(userPageName, currentSize, false);
}
if (!dataPage.mapped){
HIGH_MSG("Unable to open page %s", userPageName);
return;
}
if (master){
if (newPage){
dataAccX = Util::RelAccX(dataPage.mapped, false);
addCommonFields();
dataAccX.addField("track", RAX_32UINT);
dataAccX.addField("keynum", RAX_32UINT);
dataAccX.setRCount((currentSize - dataAccX.getOffset()) / dataAccX.getRSize());
dataAccX.setReady();
}else{
dataAccX = Util::RelAccX(dataPage.mapped);
}
}else{
dataAccX = Util::RelAccX(dataPage.mapped);
if (index == INVALID_RECORD_INDEX || reIssue){
sem.wait();
for (index = 0; index < dataAccX.getEndPos(); ++index){
if (dataAccX.getInt("status", index) == COMM_STATUS_INVALID){
// Reverse! clear entry and claim it.
dataAccX.setInt("keynum", 0, index);
dataAccX.setInt("track", 0, index);
dataAccX.setInt("killtime", 0, index);
dataAccX.setInt("pid", 0, index);
dataAccX.setInt("timer", 0, index);
dataAccX.setInt("command", 0, index);
dataAccX.setInt("status", 0, index);
break;
}
}
if (index == dataAccX.getEndPos()){dataAccX.addRecords(1);}
sem.post();
}
}
commonFieldAccess();
track = dataAccX.getFieldAccX("track");
keyNum = dataAccX.getFieldAccX("keynum");
setPid(getpid());
}
void Users::reload(const std::string &_streamName, size_t idx, uint8_t initialState){
reload(_streamName);
if (dataPage.mapped){
setTrack(idx);
setStatus(initialState);
}
}
uint32_t Users::getTrack() const{return track.uint(index);}
uint32_t Users::getTrack(size_t idx) const{return (master ? track.uint(idx) : 0);}
void Users::setTrack(uint32_t _track){track.set(_track, index);}
void Users::setTrack(uint32_t _track, size_t idx){
if (!master){return;}
track.set(_track, idx);
}
size_t Users::getKeyNum() const{return keyNum.uint(index);}
size_t Users::getKeyNum(size_t idx) const{return (master ? keyNum.uint(idx) : 0);}
void Users::setKeyNum(size_t _keyNum){keyNum.set(_keyNum, index);}
void Users::setKeyNum(size_t _keyNum, size_t idx){
if (!master){return;}
keyNum.set(_keyNum, idx);
}
}// namespace Comms

194
lib/comms.h Normal file
View file

@ -0,0 +1,194 @@
#pragma once
#include "procs.h"
#include "shared_memory.h"
#include "util.h"
#define COMM_STATUS_DONOTTRACK 0x40
#define COMM_STATUS_SOURCE 0x80
#define COMM_STATUS_DISCONNECT 0xFE
#define COMM_STATUS_INVALID 0xFF
#define COMM_LOOP(comm, onActive, onDisconnect) \
{\
for (size_t id = comm.firstValid(); id != comm.endValid(); id++){\
if (comm.getStatus(id) == COMM_STATUS_INVALID){continue;}\
onActive; \
if (!Util::Procs::isRunning(comm.getPid(id))){\
comm.setStatus(COMM_STATUS_DISCONNECT, id); \
}\
if ((comm.getTimer(id) & 0x7F) >= 126 || comm.getStatus(id) == COMM_STATUS_DISCONNECT){\
onDisconnect; \
comm.setStatus(COMM_STATUS_INVALID, id); \
}\
if ((comm.getTimer(id) & 0x7F) <= 124){\
if ((comm.getTimer(id) & 0x7F) == 124){\
HIGH_MSG("Timeout occured for entry %zu, ignoring further timeout", id); \
}\
comm.setTimer(comm.getTimer(id) + 1, id); \
}\
}\
}
namespace Comms{
class Comms{
public:
Comms();
~Comms();
operator bool() const{return dataPage.mapped;}
void addCommonFields();
void commonFieldAccess();
size_t firstValid() const;
size_t endValid() const;
void deleteFirst();
uint8_t getStatus() const;
uint8_t getStatus(size_t idx) const;
void setStatus(uint8_t _status);
void setStatus(uint8_t _status, size_t idx);
uint64_t getCommand() const;
uint64_t getCommand(size_t idx) const;
void setCommand(uint64_t _cmd);
void setCommand(uint64_t _cmd, size_t idx);
uint8_t getTimer() const;
uint8_t getTimer(size_t idx) const;
void setTimer(uint8_t _timer);
void setTimer(uint8_t _timer, size_t idx);
uint32_t getPid() const;
uint32_t getPid(size_t idx) const;
void setPid(uint32_t _pid);
void setPid(uint32_t _pid, size_t idx);
void kill(size_t idx, bool force = false);
void finishAll();
void keepAlive();
bool isAlive() const;
void setMaster(bool _master);
const std::string &pageName() const{return dataPage.name;}
protected:
bool master;
size_t index;
size_t currentSize;
IPC::semaphore sem;
IPC::sharedPage dataPage;
Util::RelAccX dataAccX;
Util::FieldAccX status;
Util::FieldAccX command;
Util::FieldAccX timer;
Util::FieldAccX pid;
Util::FieldAccX killTime;
};
class Statistics : public Comms{
public:
Statistics();
operator bool() const{return dataPage.mapped && (master || index != INVALID_RECORD_INDEX);}
void unload();
void reload(bool _master = false, bool reIssue = false);
uint8_t getSync() const;
uint8_t getSync(size_t idx) const;
void setSync(uint8_t _sync);
void setSync(uint8_t _sync, size_t idx);
uint64_t getNow() const;
uint64_t getNow(size_t idx) const;
void setNow(uint64_t _now);
void setNow(uint64_t _now, size_t idx);
uint64_t getTime() const;
uint64_t getTime(size_t idx) const;
void setTime(uint64_t _time);
void setTime(uint64_t _time, size_t idx);
uint64_t getLastSecond() const;
uint64_t getLastSecond(size_t idx) const;
void setLastSecond(uint64_t _lastSecond);
void setLastSecond(uint64_t _lastSecond, size_t idx);
uint64_t getDown() const;
uint64_t getDown(size_t idx) const;
void setDown(uint64_t _down);
void setDown(uint64_t _down, size_t idx);
uint64_t getUp() const;
uint64_t getUp(size_t idx) const;
void setUp(uint64_t _up);
void setUp(uint64_t _up, size_t idx);
std::string getHost() const;
std::string getHost(size_t idx) const;
void setHost(std::string _host);
void setHost(std::string _host, size_t idx);
std::string getStream() const;
std::string getStream(size_t idx) const;
void setStream(std::string _stream);
void setStream(std::string _stream, size_t idx);
std::string getConnector() const;
std::string getConnector(size_t idx) const;
void setConnector(std::string _connector);
void setConnector(std::string _connector, size_t idx);
uint32_t getCRC() const;
uint32_t getCRC(size_t idx) const;
void setCRC(uint32_t _crc);
void setCRC(uint32_t _crc, size_t idx);
std::string getSessId() const;
std::string getSessId(size_t index) const;
private:
Util::FieldAccX sync;
Util::FieldAccX now;
Util::FieldAccX time;
Util::FieldAccX lastSecond;
Util::FieldAccX down;
Util::FieldAccX up;
Util::FieldAccX host;
Util::FieldAccX stream;
Util::FieldAccX connector;
Util::FieldAccX crc;
};
class Users : public Comms{
public:
Users();
Users(const Users &rhs);
void reload(const std::string &_streamName = "", bool _master = false, bool reIssue = false);
void reload(const std::string &_streamName, size_t track, uint8_t initialState = 0x00);
operator bool() const{return dataPage.mapped;}
uint32_t getTrack() const;
uint32_t getTrack(size_t idx) const;
void setTrack(uint32_t _track);
void setTrack(uint32_t _track, size_t idx);
size_t getKeyNum() const;
size_t getKeyNum(size_t idx) const;
void setKeyNum(size_t _keyNum);
void setKeyNum(size_t _keyNum, size_t idx);
private:
std::string streamName;
Util::FieldAccX track;
Util::FieldAccX keyNum;
};
}// namespace Comms

View file

@ -39,6 +39,7 @@ bool Util::Config::is_restarting = false;
static Socket::Server *serv_sock_pointer = 0;
uint32_t Util::Config::printDebugLevel = DEBUG; //
std::string Util::Config::streamName;
std::string Util::Config::exitReason;
std::string Util::listenInterface;
uint32_t Util::listenPort = 0;
@ -427,6 +428,7 @@ void Util::Config::activate(){
sigaction(SIGHUP, &new_action, NULL);
sigaction(SIGTERM, &new_action, NULL);
sigaction(SIGPIPE, &new_action, NULL);
sigaction(SIGFPE, &new_action, NULL);
// check if a child signal handler isn't set already, if so, set it.
sigaction(SIGCHLD, 0, &cur_action);
if (cur_action.sa_handler == SIG_DFL || cur_action.sa_handler == SIG_IGN){
@ -448,6 +450,7 @@ void Util::Config::signal_handler(int signum, siginfo_t *sigInfo, void *ignore){
static int ctr = 0;
if (!is_active && ++ctr > 4){BACKTRACE;}
#endif
logExitReason("Setting is_active to false due to received signal interrupt");
is_active = false;
default:
switch (sigInfo->si_code){
@ -475,6 +478,7 @@ void Util::Config::signal_handler(int signum, siginfo_t *sigInfo, void *ignore){
// We ignore SIGPIPE to prevent messages triggering another SIGPIPE.
// Loops are bad, m'kay?
break;
case SIGFPE: break;
}
}// signal_handler

View file

@ -27,6 +27,10 @@ namespace Util{
static bool is_restarting; ///< Set to true when restarting, set to false on boot.
static uint32_t printDebugLevel;
static std::string streamName; ///< Used by debug messages to identify the stream name
static std::string exitReason;
static void logExitReason(const std::string &reason){
if (!exitReason.size()){exitReason = reason;}
}
// functions
Config();
Config(std::string cmd);

View file

@ -110,12 +110,13 @@ static inline void show_stackframe(){}
#endif
#ifndef SHM_DATASIZE
#define SHM_DATASIZE 20
#endif
#define DTSH_FRAGMENT_SIZE 13
#define DTSH_KEY_SIZE 25
#define DTSH_PART_SIZE 9
#define AUDIO_KEY_INTERVAL \
2000 ///< This define controls the keyframe interval for non-video tracks, such as audio and metadata tracks.
#ifndef SHM_DATASIZE
#define SHM_DATASIZE 40
#endif
#ifndef STATS_DELAY
#define STATS_DELAY 15
@ -143,10 +144,62 @@ static inline void show_stackframe(){}
/// Does not affect live streams.
#define FLIP_MIN_DURATION 20000
/// Interval where the input refreshes the user data for stats etc.
// New meta
#define SHM_STREAM_META "MstMeta%s" //%s stream name
#define SHM_STREAM_META_LEN 8 * 1024 * 1024
#define SHM_STREAM_META_ITEM 2 * 1024
#define SHM_STREAM_TM "MstTrak%s@%" PRIu32 "-%zu" //%s stream name
#define SHM_STREAM_TRACK_ITEM 16 * 1024 * 1024
#define SHM_STREAM_TRACK_LEN 4 * SHM_STREAM_TRACK_ITEM
// Default values, these will scale up and down when needed, and are mainly used as starting values.
#define DEFAULT_TRACK_COUNT 100
#define DEFAULT_FRAGMENT_COUNT 2000
#define DEFAULT_KEY_COUNT \
3 * DEFAULT_FRAGMENT_COUNT // A highest average of 5 keys / fragment is assumed
#define DEFAULT_PART_COUNT \
400 * DEFAULT_KEY_COUNT // A highest average of 500 parts / key is
// assumed
#define DEFAULT_PAGE_COUNT DEFAULT_KEY_COUNT // Assume every page is a key to ensure enough space
#define DEFAULT_FRAGMENT_DURATION 5000
#define META_META_OFFSET 104
#define META_META_RECORDSIZE 576
#define META_TRACK_OFFSET 148
#define META_TRACK_RECORDSIZE 1893
#define TRACK_TRACK_OFFSET 184
#define TRACK_TRACK_RECORDSIZE 362 + (1 * 1024 * 1024)
#define TRACK_FRAGMENT_OFFSET 68
#define TRACK_FRAGMENT_RECORDSIZE 14
#define TRACK_KEY_OFFSET 90
#define TRACK_KEY_RECORDSIZE 42
#define TRACK_PART_OFFSET 60
#define TRACK_PART_RECORDSIZE 8
#define TRACK_PAGE_OFFSET 92
#define TRACK_PAGE_RECORDSIZE 36
#define COMMS_STATISTICS "MstStat"
#define COMMS_STATISTICS_INITSIZE 8 * 1024 * 1024
#define COMMS_USERS "MstUser%s" //%s stream name
#define COMMS_USERS_INITSIZE 8 * 1024 * 1024
#define SEM_STATISTICS "/MstStat"
#define SEM_USERS "/MstUser%s" //%s stream name
#define SHM_TRACK_DATA "MstData%s@%zu_%zu" //%s stream name, %zu track ID, %PRIu32 page #
// End new meta
#define INPUT_USER_INTERVAL 1000
#define SHM_STREAM_INDEX "MstSTRM%s" //%s stream name
#define SHM_STREAM_STATE "MstSTATE%s" //%s stream name
#define SHM_STREAM_CONF "MstSCnf%s" //%s stream name
#define SHM_GLOBAL_CONF "MstGlobalConfig"
@ -157,12 +210,7 @@ static inline void show_stackframe(){}
#define STRMSTAT_READY 4
#define STRMSTAT_SHUTDOWN 5
#define STRMSTAT_INVALID 255
#define SHM_TRACK_META "MstTRAK%s@%lu" //%s stream name, %lu track ID
#define SHM_TRACK_INDEX "MstTRID%s@%lu" //%s stream name, %lu track ID
#define SHM_TRACK_INDEX_SIZE 8192
#define SHM_TRACK_DATA "MstDATA%s@%lu_%lu" //%s stream name, %lu track ID, %lu page #
#define SHM_STATISTICS "MstSTAT"
#define SHM_USERS "MstUSER%s" //%s stream name
#define SHM_TRIGGER "MstTRGR%s" //%s trigger name
#define SEM_LIVE "/MstLIVE%s" //%s stream name
#define SEM_INPUT "/MstInpt%s" //%s stream name
@ -180,7 +228,7 @@ static inline void show_stackframe(){}
#define SHM_STREAM_ENCRYPT "MstCRYP%s" //%s stream name
#define SIMUL_TRACKS 20
#define SIMUL_TRACKS 40
#ifndef UDP_API_HOST
#define UDP_API_HOST "localhost"
@ -190,8 +238,20 @@ static inline void show_stackframe(){}
#define UDP_API_PORT 4242
#endif
#define INVALID_TRACK_ID 0
// The amount of milliseconds a simulated live stream is allowed to be "behind".
// Setting this value to lower than 2 seconds **WILL** cause stuttering in playback due to buffer negotiation.
#define SIMULATED_LIVE_BUFFER 7000
#define STAT_EX_SIZE 177
#define PLAY_EX_SIZE 2 + 6 * SIMUL_TRACKS
#define INVALID_TRACK_ID 0xFFFFFFFF
#define INVALID_KEY_NUM 0xFFFFFFFF
#define INVALID_PAGE_NUM 0xFFFF
#define INVALID_RECORD_INDEX 0xFFFFFFFFFFFFFFFF
#define MAX_SIZE_T 0xFFFFFFFF
#define NEW_TRACK_ID 0x80000000
#define QUICK_NEGOTIATE 0xC0000000

View file

@ -16,11 +16,10 @@ static int on_mbedtls_wants_to_read(void *user, unsigned char *buf,
size_t len); /* Called when mbedtls wants to read data from e.g. a socket. */
static int on_mbedtls_wants_to_write(void *user, const unsigned char *buf,
size_t len); /* Called when mbedtls wants to write data to e.g. a socket. */
static std::string mbedtls_err_to_string(int r);
/* ----------------------------------------- */
DTLSSRTPHandshake::DTLSSRTPHandshake() : write_callback(NULL), cert(NULL), key(NULL){
DTLSSRTPHandshake::DTLSSRTPHandshake() : cert(NULL), key(NULL), write_callback(NULL){
memset((void *)&entropy_ctx, 0x00, sizeof(entropy_ctx));
memset((void *)&rand_ctx, 0x00, sizeof(rand_ctx));
memset((void *)&ssl_ctx, 0x00, sizeof(ssl_ctx));
@ -381,7 +380,7 @@ static void print_mbedtls_error(int r){
}
static void print_mbedtls_debug_message(void *ctx, int level, const char *file, int line, const char *str){
DONTEVEN_MSG("%s:%04d: %.*s", file, line, strlen(str) - 1, str);
DONTEVEN_MSG("%s:%04d: %.*s", file, line, (int)strlen(str) - 1, str);
#if LOG_TO_FILE
static std::ofstream ofs;
@ -392,19 +391,4 @@ static void print_mbedtls_debug_message(void *ctx, int level, const char *file,
#endif
}
static std::string mbedtls_err_to_string(int r){
switch (r){
case MBEDTLS_ERR_SSL_WANT_READ:{
return "MBEDTLS_ERR_SSL_WANT_READ";
}
case MBEDTLS_ERR_SSL_WANT_WRITE:{
return "MBEDTLS_ERR_SSL_WANT_WRITE";
}
default:{
print_mbedtls_error(r);
return "UNKNOWN";
}
}
}
/* ---------------------------------------- */

View file

@ -18,8 +18,11 @@
class DTLSSRTPHandshake{
public:
DTLSSRTPHandshake();
int init(mbedtls_x509_crt *certificate,
mbedtls_pk_context *privateKey, int (*writeCallback)(const uint8_t *data, int *nbytes)); // writeCallback should return 0 on succes < 0 on error. nbytes holds the number of bytes to be sent and needs to be set to the number of bytes actually sent.
int init(mbedtls_x509_crt *certificate, mbedtls_pk_context *privateKey,
int (*writeCallback)(const uint8_t *data,
int *nbytes)); // writeCallback should return 0 on succes < 0 on error.
// nbytes holds the number of bytes to be sent and needs
// to be set to the number of bytes actually sent.
int shutdown();
int parse(const uint8_t *data, size_t nbytes);
bool hasKeyingMaterial();
@ -40,8 +43,9 @@ private:
public:
int (*write_callback)(const uint8_t *data, int *nbytes);
std::deque<uint8_t> buffer; /* Accessed from BIO callbback. We copy the bytes you pass into `parse()` into this temporary buffer which is read by a trigger to `mbedlts_ssl_handshake()`. */
std::string cipher; /* selected SRTP cipher. */
std::deque<uint8_t> buffer; /* Accessed from BIO callbback. We copy the bytes you pass into `parse()` into this
temporary buffer which is read by a trigger to `mbedlts_ssl_handshake()`. */
std::string cipher; /* selected SRTP cipher. */
std::string remote_key;
std::string remote_salt;
std::string local_key;

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,12 @@
/// Holds all headers for DDVTECH Stream Container parsing/generation.
#pragma once
#include "defines.h"
#include "json.h"
#include "shared_memory.h"
#include "socket.h"
#include "timing.h"
#include "util.h"
#include <deque>
#include <iostream>
#include <set>
@ -28,6 +31,8 @@
namespace DTSC{
extern uint64_t veryUglyJitterOverride;
///\brief This enum holds all possible datatypes for DTSC packets.
enum datatype{
AUDIO, ///< Stream Audio data
@ -43,26 +48,6 @@ namespace DTSC{
extern char Magic_Packet2[]; ///< The magic bytes for a DTSC packet version 2
extern char Magic_Command[]; ///< The magic bytes for a DTCM packet
///\brief A simple structure used for ordering byte seek positions.
struct seekPos{
///\brief Less-than comparison for seekPos structures.
///\param rhs The seekPos to compare with.
///\return Whether this object is smaller than rhs.
bool operator<(const seekPos &rhs) const{
if (seekTime < rhs.seekTime){
return true;
}else{
if (seekTime == rhs.seekTime){
if (trackID < rhs.trackID){return true;}
}
}
return false;
}
long long unsigned int seekTime; ///< Stores the timestamp of the DTSC packet referenced by this structure.
long long unsigned int bytePos; ///< Stores the byteposition of the DTSC packet referenced by this structure.
unsigned int trackID; ///< Stores the track the DTSC packet referenced by this structure is associated with.
};
enum packType{DTSC_INVALID, DTSC_HEAD, DTSC_V1, DTSC_V2, DTCM};
/// This class allows scanning through raw binary format DTSC data.
@ -78,8 +63,6 @@ namespace DTSC{
Scan getMember(const std::string &indice) const;
Scan getMember(const char *indice) const;
Scan getMember(const char *indice, size_t ind_len) const;
void nullMember(const std::string &indice);
void nullMember(const char *indice, size_t ind_len);
Scan getIndice(size_t num) const;
std::string getIndiceName(size_t num) const;
size_t getSize() const;
@ -104,7 +87,7 @@ namespace DTSC{
class Packet{
public:
Packet();
Packet(const Packet &rhs);
Packet(const Packet &rhs, size_t idx = INVALID_TRACK_ID);
Packet(const char *data_, unsigned int len, bool noCopy = false);
virtual ~Packet();
void null();
@ -113,9 +96,8 @@ namespace DTSC{
packType getVersion() const;
void reInit(Socket::Connection &src);
void reInit(const char *data_, unsigned int len, bool noCopy = false);
void genericFill(long long packTime, long long packOffset, long long packTrack,
const char *packData, long long packDataSize, uint64_t packBytePos,
bool isKeyframe, int64_t bootMsOffset = 0);
void genericFill(uint64_t packTime, int64_t packOffset, uint32_t packTrack, const char *packData,
size_t packDataSize, uint64_t packBytePos, bool isKeyframe);
void appendData(const char *appendData, uint32_t appendLen);
void getString(const char *identifier, char *&result, size_t &len) const;
void getString(const char *identifier, std::string &result) const;
@ -129,7 +111,6 @@ namespace DTSC{
void setKeyFrame(bool kf);
virtual uint64_t getTime() const;
void setTime(uint64_t _time);
void nullMember(const std::string &memb);
size_t getTrackId() const;
char *getData() const;
size_t getDataLen() const;
@ -139,7 +120,6 @@ namespace DTSC{
JSON::Value toJSON() const;
std::string toSummary() const;
Scan getScan() const;
Scan getScan();
protected:
bool master;
@ -161,315 +141,358 @@ namespace DTSC{
: Packet(data_, len, noCopy){
timeOverride = reTime;
}
~RetimedPacket(){}
virtual uint64_t getTime() const{return timeOverride;}
protected:
uint64_t timeOverride;
};
/// A simple structure used for ordering byte seek positions.
struct livePos{
livePos(){
seekTime = 0;
trackID = 0;
}
livePos(const livePos &rhs){
seekTime = rhs.seekTime;
trackID = rhs.trackID;
}
void operator=(const livePos &rhs){
seekTime = rhs.seekTime;
trackID = rhs.trackID;
}
bool operator==(const livePos &rhs){
return seekTime == rhs.seekTime && trackID == rhs.trackID;
}
bool operator!=(const livePos &rhs){
return seekTime != rhs.seekTime || trackID != rhs.trackID;
}
bool operator<(const livePos &rhs) const{
if (seekTime < rhs.seekTime){
return true;
}else{
if (seekTime > rhs.seekTime){return false;}
}
return (trackID < rhs.trackID);
}
long long unsigned int seekTime;
unsigned int trackID;
};
/*LTS-START*/
///\brief Basic class supporting initialization Vectors.
///
/// These are used for encryption of data.
class Ivec{
class Parts{
public:
Ivec();
Ivec(long long int iVec);
void setIvec(long long int iVec);
void setIvec(std::string iVec);
void setIvec(const char *iVec, int len);
long long int asInt();
char *getData();
Parts(const Util::RelAccX &_parts);
size_t getFirstValid() const;
size_t getEndValid() const;
size_t getValidCount() const;
size_t getSize(size_t idx) const;
uint64_t getDuration(size_t idx) const;
int64_t getOffset(size_t idx) const;
private:
///\brief Data storage for this initialization vector.
///
/// - 8 bytes: MSB storage of the initialization vector.
char data[8];
const Util::RelAccX &parts;
Util::RelAccXFieldData sizeField;
Util::RelAccXFieldData durationField;
Util::RelAccXFieldData offsetField;
};
/*LTS-END*/
///\brief Basic class for storage of data associated with single DTSC packets, a.k.a. parts.
class Part{
class Keys{
public:
uint32_t getSize();
void setSize(uint32_t newSize);
uint32_t getDuration();
void setDuration(uint32_t newDuration);
uint32_t getOffset();
void setOffset(uint32_t newOffset);
char *getData();
void toPrettyString(std::ostream &str, int indent = 0);
Keys(Util::RelAccX &_keys);
Keys(const Util::RelAccX &_keys);
size_t getFirstValid() const;
size_t getEndValid() const;
size_t getValidCount() const;
size_t getFirstPart(size_t idx) const;
size_t getBpos(size_t idx) const;
uint64_t getDuration(size_t idx) const;
size_t getNumber(size_t idx) const;
size_t getParts(size_t idx) const;
uint64_t getTime(size_t idx) const;
void setSize(size_t idx, size_t _size);
size_t getSize(size_t idx) const;
size_t getNumForTime(uint64_t time) const;
private:
#define PACKED_PART_SIZE 9
///\brief Data storage for this Part.
///
/// - 3 bytes: MSB storage of the payload size of this packet in bytes.
/// - 3 bytes: MSB storage of the duration of this packet in milliseconds.
/// - 3 bytes: MSB storage of the presentation time offset of this packet in milliseconds.
char data[PACKED_PART_SIZE];
bool isConst;
Util::RelAccX empty;
Util::RelAccX &keys;
const Util::RelAccX &cKeys;
Util::RelAccXFieldData firstPartField;
Util::RelAccXFieldData bposField;
Util::RelAccXFieldData durationField;
Util::RelAccXFieldData numberField;
Util::RelAccXFieldData partsField;
Util::RelAccXFieldData timeField;
Util::RelAccXFieldData sizeField;
};
///\brief Basic class for storage of data associated with keyframes.
///
/// When deleting this object, make sure to remove all DTSC::Part associated with it, if any. If you fail doing this, it *will* cause data corruption.
class Key{
class Fragments{
public:
unsigned long long getBpos();
void setBpos(unsigned long long newBpos);
unsigned long getLength();
void setLength(unsigned long newLength);
unsigned long getNumber();
void setNumber(unsigned long newNumber);
unsigned short getParts();
void setParts(unsigned short newParts);
unsigned long long getTime();
void setTime(unsigned long long newTime);
char *getData();
void toPrettyString(std::ostream &str, int indent = 0);
Fragments(const Util::RelAccX &_fragments);
size_t getFirstValid() const;
size_t getEndValid() const;
size_t getValidCount() const;
uint64_t getDuration(size_t idx) const;
size_t getKeycount(size_t idx) const;
size_t getFirstKey(size_t idx) const;
size_t getSize(size_t idx) const;
private:
#define PACKED_KEY_SIZE 25
///\brief Data storage for this Key.
///
/// - 8 bytes: MSB storage of the position of the first packet of this keyframe within the file.
/// - 3 bytes: MSB storage of the duration of this keyframe.
/// - 4 bytes: MSB storage of the number of this keyframe.
/// - 2 bytes: MSB storage of the amount of parts in this keyframe.
/// - 8 bytes: MSB storage of the timestamp associated with this keyframe's first packet.
char data[PACKED_KEY_SIZE];
const Util::RelAccX &fragments;
};
///\brief Basic class for storage of data associated with fragments.
class Fragment{
public:
unsigned long getDuration();
void setDuration(unsigned long newDuration);
char getLength();
void setLength(char newLength);
unsigned long getNumber();
void setNumber(unsigned long newNumber);
unsigned long getSize();
void setSize(unsigned long newSize);
char *getData();
void toPrettyString(std::ostream &str, int indent = 0);
private:
#define PACKED_FRAGMENT_SIZE 13
///\brief Data storage for this Fragment.
///
/// - 4 bytes: duration (in milliseconds)
/// - 1 byte: length (amount of keyframes)
/// - 4 bytes: number of first keyframe in fragment
/// - 4 bytes: size of fragment in bytes
char data[PACKED_FRAGMENT_SIZE];
};
///\brief Class for storage of track data
class Track{
public:
Track();
Track(JSON::Value &trackRef);
Track(Scan &trackRef);
void clearParts();
Util::RelAccX parts;
Util::RelAccX keys;
Util::RelAccX fragments;
inline operator bool() const{
return (parts.size() && keySizes.size() && (keySizes.size() == keys.size()));
}
/*
void update(long long packTime, long long packOffset, long long packDataSize, uint64_t
packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size = 1900);
*/
void update(long long packTime, long long packOffset, long long packDataSize,
uint64_t packBytePos, bool isKeyframe, long long packSendSize,
unsigned long segment_size = 1900, const char *iVec = 0);
int getSendLen(bool skipDynamic = false);
void send(Socket::Connection &conn, bool skipDynamic = false);
void writeTo(char *&p);
JSON::Value toJSON(bool skipDynamic = false);
std::deque<Fragment> fragments;
std::deque<Key> keys;
std::deque<unsigned long> keySizes;
std::deque<Part> parts;
std::deque<Ivec> ivecs; /*LTS*/
Key &getKey(unsigned int keyNum);
Fragment &getFrag(unsigned int fragNum);
unsigned int timeToKeynum(unsigned int timestamp);
uint32_t timeToFragnum(uint64_t timestamp);
void reset();
void toPrettyString(std::ostream &str, int indent = 0, int verbosity = 0);
void finalize();
uint32_t biggestFragment();
Util::RelAccX pages;
std::string getIdentifier();
std::string getWritableIdentifier();
unsigned int trackID;
uint64_t firstms;
uint64_t lastms;
int bps;
int max_bps;
int missedFrags;
std::string init;
std::string codec;
std::string type;
std::string lang; ///< ISO 639-2 Language of track, empty or und if unknown.
uint32_t minKeepAway; ///< Time in MS to never seek closer than live point to
// audio only
int rate;
int size;
int channels;
// video only
int width;
int height;
int fpks;
void removeFirstKey();
uint32_t secsSinceFirstFragmentInsert();
Util::RelAccX track;
private:
std::string cachedIdent;
std::deque<uint32_t> fragInsertTime;
// Internal buffers so we don't always need to search for everything
Util::RelAccXFieldData trackIdField;
Util::RelAccXFieldData trackTypeField;
Util::RelAccXFieldData trackCodecField;
Util::RelAccXFieldData trackFirstmsField;
Util::RelAccXFieldData trackLastmsField;
Util::RelAccXFieldData trackBpsField;
Util::RelAccXFieldData trackMaxbpsField;
Util::RelAccXFieldData trackLangField;
Util::RelAccXFieldData trackInitField;
Util::RelAccXFieldData trackRateField;
Util::RelAccXFieldData trackSizeField;
Util::RelAccXFieldData trackChannelsField;
Util::RelAccXFieldData trackWidthField;
Util::RelAccXFieldData trackHeightField;
Util::RelAccXFieldData trackFpksField;
Util::RelAccXFieldData trackMissedFragsField;
Util::RelAccXFieldData partSizeField;
Util::RelAccXFieldData partDurationField;
Util::RelAccXFieldData partOffsetField;
Util::RelAccXFieldData keyFirstPartField;
Util::RelAccXFieldData keyBposField;
Util::RelAccXFieldData keyDurationField;
Util::RelAccXFieldData keyNumberField;
Util::RelAccXFieldData keyPartsField;
Util::RelAccXFieldData keyTimeField;
Util::RelAccXFieldData keySizeField;
Util::RelAccXFieldData fragmentDurationField;
Util::RelAccXFieldData fragmentKeysField;
Util::RelAccXFieldData fragmentFirstKeyField;
Util::RelAccXFieldData fragmentSizeField;
};
///\brief Class for storage of meta data
class Meta{
/// \todo Make toJSON().toNetpacked() shorter
public:
Meta();
Meta(const DTSC::Packet &source);
Meta(JSON::Value &meta);
bool nextIsKey;
Meta(const std::string &_streamName, const DTSC::Scan &src);
Meta(const std::string &_streamName = "", bool master = true);
Meta(const std::string &_streamName, const std::string &fileName);
inline operator bool() const{// returns if the object contains valid meta data BY LOOKING AT vod/live FLAGS
return vod || live;
}
void reinit(const DTSC::Packet &source);
void update(const DTSC::Packet &pack, unsigned long segment_size = 1900);
void updatePosOverride(DTSC::Packet &pack, uint64_t bpos);
void update(JSON::Value &pack, unsigned long segment_size = 1900);
/*LTS
void update(long long packTime, long long packOffset, long long packTrack, long long
packDataSize, uint64_t packBytePos, bool isKeyframe, long long packSendSize = 0, unsigned long
segment_size = 1900); LTS*/
void update(long long packTime, long long packOffset, long long packTrack,
long long packDataSize, uint64_t packBytePos, bool isKeyframe,
long long packSendSize = 0, unsigned long segment_size = 1900, const char *iVec = 0);
unsigned int getSendLen(bool skipDynamic = false,
std::set<unsigned long> selectedTracks = std::set<unsigned long>());
void send(Socket::Connection &conn, bool skipDynamic = false,
std::set<unsigned long> selectedTracks = std::set<unsigned long>());
void writeTo(char *p);
JSON::Value toJSON();
void reset();
bool toFile(const std::string &fileName);
void toPrettyString(std::ostream &str, int indent = 0, int verbosity = 0);
// members:
std::map<unsigned int, Track> tracks;
Track &mainTrack();
uint32_t biggestFragment();
bool vod;
bool live;
bool merged;
uint16_t version;
int64_t moreheader;
int64_t bufferWindow;
int64_t bootMsOffset; ///< Millis to add to packet timestamps to get millis since system boot.
std::string sourceURI;
JSON::Value inputLocalVars;
};
~Meta();
void reInit(const std::string &_streamName, bool master = true);
void reInit(const std::string &_streamName, const std::string &fileName);
void reInit(const std::string &_streamName, const DTSC::Scan &src);
/// An iterator helper for easily iterating over the parts in a Fragment.
class PartIter{
public:
PartIter(Track &Trk, Fragment &frag);
Part &operator*() const; ///< Dereferences into a Value reference.
Part *operator->() const; ///< Dereferences into a Value reference.
operator bool() const; ///< True if not done iterating.
PartIter &operator++(); ///< Go to next iteration.
private:
uint32_t lastKey;
uint32_t currInKey;
Track *tRef;
std::deque<Part>::iterator pIt;
std::deque<Key>::iterator kIt;
};
void refresh();
/// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it.
class File{
public:
File();
File(const File &rhs);
File(std::string filename, bool create = false);
File &operator=(const File &rhs);
operator bool() const;
~File();
Meta &getMeta();
long long int getLastReadPos();
bool writeHeader(std::string &header, bool force = false);
long long int addHeader(std::string &header);
long int getBytePosEOF();
long int getBytePos();
bool reachedEOF();
void seekNext();
void parseNext();
DTSC::Packet &getPacket();
bool seek_time(unsigned int ms);
bool seek_time(unsigned int ms, unsigned int trackNo, bool forceSeek = false);
bool seek_bpos(int bpos);
void rewritePacket(std::string &newPacket, int bytePos);
void writePacket(std::string &newPacket);
void writePacket(JSON::Value &newPacket);
bool atKeyframe();
void selectTracks(std::set<unsigned long> &tracks);
void setMaster(bool _master);
bool getMaster() const;
void clear();
void minimalFrom(const Meta &src);
bool trackLoaded(size_t idx) const;
bool trackValid(size_t idx) const;
size_t trackCount() const;
size_t addCopy(size_t source);
size_t addDelayedTrack(size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
size_t partCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT);
size_t addTrack(size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
size_t partCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT,
bool setValid = true);
void resizeTrack(size_t source, size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
size_t partCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT);
void initializeTrack(Track &t, size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
size_t parCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT);
void merge(const DTSC::Meta &M, bool deleteTracks = true, bool copyData = true);
void updatePosOverride(DTSC::Packet &pack, uint64_t bpos);
void update(const DTSC::Packet &pack);
void update(uint64_t packTime, int64_t packOffset, uint32_t packTrack, uint64_t packDataSize,
uint64_t packBytePos, bool isKeyframe, uint64_t packSendSize = 0);
size_t trackIDToIndex(size_t trackID, size_t pid = 0) const;
std::string getTrackIdentifier(size_t idx, bool unique = false) const;
void setInit(size_t trackIdx, const std::string &init);
void setInit(size_t trackIdx, const char *init, size_t initLen);
std::string getInit(size_t idx) const;
void setSource(const std::string &src);
std::string getSource() const;
void setID(size_t trackIdx, size_t id);
size_t getID(size_t trackIdx) const;
void markUpdated(size_t trackIdx);
uint64_t getLastUpdated(size_t trackIdx) const;
uint64_t getLastUpdated() const;
void setChannels(size_t trackIdx, uint16_t channels);
uint16_t getChannels(size_t trackIdx) const;
void setRate(size_t trackIdx, uint32_t rate);
uint32_t getRate(size_t trackIdx) const;
void setWidth(size_t trackIdx, uint32_t width);
uint32_t getWidth(size_t trackIdx) const;
void setHeight(size_t trackIdx, uint32_t height);
uint32_t getHeight(size_t trackIdx) const;
void setSize(size_t trackIdx, uint16_t size);
uint16_t getSize(size_t trackIdx) const;
void setType(size_t trackIdx, const std::string &type);
std::string getType(size_t trackIdx) const;
void setCodec(size_t trackIdx, const std::string &codec);
std::string getCodec(size_t trackIdx) const;
void setLang(size_t trackIdx, const std::string &lang);
std::string getLang(size_t trackIdx) const;
void setFirstms(size_t trackIdx, uint64_t firstms);
uint64_t getFirstms(size_t trackIdx) const;
void setLastms(size_t trackIdx, uint64_t lastms);
uint64_t getLastms(size_t trackIdx) const;
uint64_t getDuration(size_t trackIdx) const;
void setBps(size_t trackIdx, uint64_t bps);
uint64_t getBps(size_t trackIdx) const;
void setMaxBps(size_t trackIdx, uint64_t bps);
uint64_t getMaxBps(size_t trackIdx) const;
void setFpks(size_t trackIdx, uint64_t bps);
uint64_t getFpks(size_t trackIdx) const;
void setMissedFragments(size_t trackIdx, uint32_t missedFragments);
uint32_t getMissedFragments(size_t trackIdx) const;
void setMinKeepAway(size_t trackIdx, uint64_t minKeepAway);
uint64_t getMinKeepAway(size_t trackIdx) const;
/*LTS-START*/
void setSourceTrack(size_t trackIdx, size_t sourceTrack);
uint64_t getSourceTrack(size_t trackIdx) const;
void setEncryption(size_t trackIdx, const std::string &encryption);
std::string getEncryption(size_t trackIdx) const;
void setPlayReady(size_t trackIdx, const std::string &playReady);
std::string getPlayReady(size_t trackIdx) const;
void setWidevine(size_t trackIdx, const std::string &widevine);
std::string getWidevine(size_t trackIdx) const;
void setIvec(size_t trackIdx, uint64_t ivec);
uint64_t getIvec(size_t trackIdx) const;
void setMinimumFragmentDuration(uint64_t newFragmentDuration = DEFAULT_FRAGMENT_DURATION);
uint64_t getMinimumFragmentDuration() const;
/*LTS-END*/
/*LTS-START
uint64_t getFragmentDuration() const{return DEFAULT_FRAGMENT_DURATION;}
LTS-END*/
void setVod(bool vod = true);
bool getVod() const;
void setLive(bool live = true);
bool getLive() const;
bool hasBFrames(size_t idx = INVALID_TRACK_ID) const;
void setBufferWindow(uint64_t bufferWindow);
uint64_t getBufferWindow() const;
void setBootMsOffset(uint64_t bootMsOffset);
uint64_t getBootMsOffset() const;
std::set<size_t> getValidTracks(bool skipEmpty = false) const;
std::set<size_t> getMySourceTracks(size_t pid) const;
void validateTrack(size_t trackIdx);
void removeEmptyTracks();
void removeTrack(size_t trackIdx);
void removeFirstKey(size_t trackIdx);
size_t mainTrack() const;
uint32_t biggestFragment(uint32_t idx = INVALID_TRACK_ID) const;
bool tracksAlign(size_t idx1, size_t idx2) const;
uint64_t getTimeForFragmentIndex(uint32_t idx, uint32_t fragmentIdx) const;
uint32_t getFragmentIndexForTime(uint32_t idx, uint64_t timestamp) const;
uint64_t getTimeForKeyIndex(uint32_t idx, uint32_t keyIdx) const;
uint32_t getKeyIndexForTime(uint32_t idx, uint64_t timestamp) const;
uint32_t getPartIndex(const DTSC::Packet &pack, size_t idx) const;
bool nextPageAvailable(uint32_t idx, size_t currentPage) const;
size_t getPageNumberForTime(uint32_t idx, uint64_t time) const;
size_t getPageNumberForKey(uint32_t idx, uint64_t keynumber) const;
const Util::RelAccX &parts(size_t idx) const;
Util::RelAccX &keys(size_t idx);
const Util::RelAccX &keys(size_t idx) const;
const Util::RelAccX &fragments(size_t idx) const;
Util::RelAccX &pages(size_t idx);
const Util::RelAccX &pages(size_t idx) const;
std::string toPrettyString() const;
void remap(const std::string &_streamName = "");
uint64_t getSendLen(bool skipDynamic = false, std::set<size_t> selectedTracks = std::set<size_t>()) const;
void toFile(const std::string &fName) const;
void send(Socket::Connection &conn, bool skypDynamic = false,
std::set<size_t> selectedTracks = std::set<size_t>(), bool reID = false) const;
void toJSON(JSON::Value &res, bool skipDynamic = true, bool tracksOnly = false) const;
std::string getStreamName() const{return streamName;}
JSON::Value inputLocalVars;
uint8_t version;
protected:
void sBufMem(size_t trackCount = DEFAULT_TRACK_COUNT);
void sBufShm(const std::string &_streamName, size_t trackCount = DEFAULT_TRACK_COUNT, bool master = true);
void streamInit(size_t trackCount = DEFAULT_TRACK_COUNT);
std::string streamName;
IPC::sharedPage streamPage;
Util::RelAccX stream;
Util::RelAccX trackList;
std::map<size_t, Track> tracks;
std::map<size_t, IPC::sharedPage> tM;
bool isMaster;
char *streamMemBuf;
bool isMemBuf;
std::map<size_t, char *> tMemBuf;
std::map<size_t, size_t> sizeMemBuf;
private:
long int endPos;
void readHeader(int pos);
DTSC::Packet myPack;
Meta metadata;
std::map<unsigned int, std::string> trackMapping;
long long int currtime;
long long int lastreadpos;
int currframe;
FILE *F;
unsigned long headerSize;
void *buffer;
bool created;
std::set<seekPos> currentPositions;
std::set<unsigned long> selectedTracks;
};
// FileWriter
// Internal buffers so we don't always need to search for everything
Util::RelAccXFieldData streamVodField;
Util::RelAccXFieldData streamLiveField;
Util::RelAccXFieldData streamSourceField;
Util::RelAccXFieldData streamBufferWindowField;
Util::RelAccXFieldData streamBootMsOffsetField;
Util::RelAccXFieldData streamMinimumFragmentDurationField;
Util::RelAccXFieldData trackValidField;
Util::RelAccXFieldData trackIdField;
Util::RelAccXFieldData trackTypeField;
Util::RelAccXFieldData trackCodecField;
Util::RelAccXFieldData trackPageField;
Util::RelAccXFieldData trackLastUpdateField;
Util::RelAccXFieldData trackPidField;
Util::RelAccXFieldData trackMinKeepAwayField;
Util::RelAccXFieldData trackSourceTidField;
Util::RelAccXFieldData trackEncryptionField;
Util::RelAccXFieldData trackIvecField;
Util::RelAccXFieldData trackWidevineField;
Util::RelAccXFieldData trackPlayreadyField;
};
}// namespace DTSC

File diff suppressed because it is too large Load diff

View file

@ -1,225 +1,170 @@
#include "auth.h"
#include "bitfields.h"
#include "defines.h"
#include "encode.h"
#include "encryption.h"
#include "http_parser.h"
#include "nal.h" /*LTS*/
#include "rijndael.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include "h264.h"
namespace Encryption{
/// helper function for printing binary values
std::string hexString(const char *data, unsigned long dataLen){
std::stringstream res;
for (int i = 0; i < dataLen; i++){
res << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
if (i % 4 == 3){res << " ";}
AES::AES(){mbedtls_aes_init(&ctx);}
AES::~AES(){mbedtls_aes_free(&ctx);}
void AES::setEncryptKey(const char *key){
mbedtls_aes_setkey_enc(&ctx, (const unsigned char *)key, 128);
}
void AES::setDecryptKey(const char *key){
mbedtls_aes_setkey_dec(&ctx, (const unsigned char *)key, 128);
}
DTSC::Packet AES::encryptPacketCTR(const DTSC::Meta &M, const DTSC::Packet &src, uint64_t ivec, size_t newTrack){
DTSC::Packet res;
if (newTrack == INVALID_TRACK_ID){
FAIL_MSG("No target track given for track encryption!");
return res;
}
return res.str();
}
std::string AES_Crypt(const std::string &data, const std::string &key, std::string &ivec){
return AES_Crypt(data.data(), data.size(), key.data(), ivec.data());
}
std::string AES_Crypt(const char *data, int dataLen, const char *key, const char *ivec){
char *outData = (char *)malloc(dataLen * sizeof(char));
memcpy(outData, data, dataLen);
AESFullCrypt(outData, dataLen, key, ivec);
std::string result = std::string(outData, dataLen);
free(outData);
return result;
}
/// This function encrypts data in-place.
/// It alters all parameters except dataLen.
/// Do not use it unless you know what you are doing.
void AESPartialCrypt(char *data, int dataLen, char *expandedKey, char *eCount, char *iVec,
unsigned int &num, bool &initialize){
if (initialize){
num = 0;
memset(eCount, 0, 16);
/// Before use, make sure the iVec is in the UPPER 8 bytes
memset(iVec + 8, 0, 8);
/// Before use, make sure this is not the only copy of the key you had. It is lost upon initialization
char cryptKey[224];
AES_set_encrypt_key(expandedKey, 128, cryptKey);
memcpy(expandedKey, cryptKey, 224);
initialize = false;
}
char *outData = (char *)malloc(dataLen * sizeof(char));
AES_CTR128_crypt(data, outData, dataLen, expandedKey, iVec, eCount, num);
memcpy(data, outData, dataLen);
free(outData);
}
// Generates the contentkey from a keyseed and a keyid
std::string PR_GenerateContentKey(std::string &keyseed, std::string &keyid){
char contentKey[16];
char dataBlob[92];
char keyA[32], keyB[32], keyC[32];
std::string keyidBytes = PR_GuidToByteArray(keyid);
memcpy(dataBlob, keyseed.data(), 30);
memcpy(dataBlob + 30, keyidBytes.data(), 16);
memcpy(dataBlob + 46, keyseed.data(), 30);
memcpy(dataBlob + 76, keyidBytes.data(), 16);
// KeyA is generated from keyseed/keyid
Secure::sha256bin(dataBlob, 46, keyA);
// KeyB is generated from keyseed/keyid/keyseed
Secure::sha256bin(dataBlob, 76, keyB);
// KeyC is generated from keyseed/keyid/keyseed/keyid
Secure::sha256bin(dataBlob, 92, keyC);
for (int i = 0; i < 16; i++){
contentKey[i] = keyA[i] ^ keyA[i + 16] ^ keyB[i] ^ keyB[i + 16] ^ keyC[i] ^ keyC[i + 16];
}
return std::string(contentKey, 16);
}
// Transforms a guid to the MS byte array representation
std::string PR_GuidToByteArray(std::string &guid){
char result[16];
result[0] = guid[3];
result[1] = guid[2];
result[2] = guid[1];
result[3] = guid[0];
result[4] = guid[5];
result[5] = guid[4];
result[6] = guid[7];
result[7] = guid[6];
memcpy(result + 8, guid.data() + 8, 8);
return std::string(result, 8);
}
/// This function encrypts data in-place.
void AESFullCrypt(char *data, int dataLen, const char *key, const char *ivec){
unsigned int num = 0;
char expandedKey[224];
memcpy(expandedKey, key, 16);
char eCount[16];
char iVec[16];
memcpy(iVec, ivec, 8);
bool initialize = true;
AESPartialCrypt(data, dataLen, expandedKey, eCount, iVec, num, initialize);
}
void encryptPlayReady(DTSC::Packet &thisPack, std::string &codec, const char *iVec, const char *key){
char *data;
size_t dataLen;
thisPack.getString("data", data, dataLen);
src.getString("data", data, dataLen);
if (codec == "H264"){
unsigned int num = 0;
char expandedKey[224];
memcpy(expandedKey, key, 16);
char eCount[16];
char initVec[16];
memcpy(initVec, iVec, 8);
bool initialize = true;
size_t trackIdx = M.getSourceTrack(newTrack);
int pos = 0;
char *encData = (char *)malloc(dataLen);
std::deque<int> nalSizes = nalu::parseNalSizes(thisPack);
for (std::deque<int>::iterator it = nalSizes.begin(); it != nalSizes.end(); it++){
int encrypted = (*it - 5) & ~0xF; // Bitmask to a multiple of 16
int clear = *it - encrypted;
Encryption::AESPartialCrypt(data + pos + clear, encrypted, expandedKey, eCount, initVec, num, initialize);
pos += *it;
}
size_t dataOffset = 0;
if (M.getType(trackIdx) == "video" && dataLen > 96){
dataOffset = dataLen - (int((dataLen - 96) / 16) * 16);
memcpy(encData, data, dataOffset);
}
if (codec == "AAC"){Encryption::AESFullCrypt(data, dataLen, key, iVec);}
}
/// Converts a hexidecimal string format key to binary string format.
std::string binKey(std::string hexkey){
char newkey[16];
memset(newkey, 0, 16);
for (size_t i = 0; i < hexkey.size(); ++i){
char c = hexkey[i];
newkey[i >> 1] |= ((c & 15) + (((c & 64) >> 6) | ((c & 64) >> 3))) << ((~i & 1) << 2);
if (!encryptBlockCTR(ivec, data + dataOffset, encData + dataOffset, dataLen - dataOffset)){
FAIL_MSG("Failed to encrypt packet");
free(encData);
return res;
}
return std::string(newkey, 16);
}
/// Helper function for urlescape.
/// Encodes a character as two hex digits.
std::string hex(char dec){
char dig1 = (dec & 0xF0) >> 4;
char dig2 = (dec & 0x0F);
if (dig1 <= 9) dig1 += 48;
if (10 <= dig1 && dig1 <= 15) dig1 += 97 - 10;
if (dig2 <= 9) dig2 += 48;
if (10 <= dig2 && dig2 <= 15) dig2 += 97 - 10;
std::string r;
r.append(&dig1, 1);
r.append(&dig2, 1);
return r;
}
std::string hex(const std::string &input){
std::string res;
res.reserve(input.size() * 2);
for (unsigned int i = 0; i < input.size(); i++){res += hex(input[i]);}
res.genericFill(src.getTime(), src.getInt("offset"), newTrack, encData, dataLen, 0, src.getFlag("keyframe"));
free(encData);
return res;
}
void fillVerimatrix(verimatrixData &vmData){
int hostPos = vmData.url.find("://") + 3;
int portPos = vmData.url.find(":", hostPos);
std::string hostName =
vmData.url.substr(hostPos, (portPos == std::string::npos ? portPos : portPos - hostPos));
int port = (portPos == std::string::npos ? 80 : atoi(vmData.url.data() + portPos + 1));
Socket::Connection veriConn(hostName, port, true);
HTTP::Parser H;
H.url = "/CAB/keyfile?PROTECTION-TYPE=PLAYREADY&TYPE=DTV&POSITION=0&RESOURCE-ID=" + vmData.name;
H.SetHeader("Host", vmData.url.substr(hostPos));
H.SendRequest(veriConn);
H.Clean();
while (veriConn && (!veriConn.spool() || !H.Read(veriConn))){}
vmData.key = binKey(H.GetHeader("Key"));
vmData.keyid = H.GetHeader("KeyId");
vmData.laurl = H.GetHeader("LAURL");
vmData.lauurl = H.GetHeader("LAUURL");
std::string AES::encryptBlockCTR(uint64_t ivec, const std::string &inp){
char *resPtr = (char *)malloc(inp.size());
if (!encryptBlockCTR(ivec, inp.c_str(), resPtr, inp.size())){
free(resPtr);
return "";
}
std::string result(resPtr, inp.size());
free(resPtr);
return result;
}
void verimatrixData::read(const char *shmPage){
int offset = 0;
url = std::string(shmPage + offset);
offset += url.size() + 1; //+1 for the concluding 0-byte
name = std::string(shmPage + offset);
offset += name.size() + 1; //+1 for the concluding 0-byte
key = std::string(shmPage + offset);
offset += key.size() + 1; //+1 for the concluding 0-byte
keyid = std::string(shmPage + offset);
offset += keyid.size() + 1; //+1 for the concluding 0-byte
laurl = std::string(shmPage + offset);
offset += laurl.size() + 1; //+1 for the concluding 0-byte
lauurl = std::string(shmPage + offset);
bool AES::encryptBlockCTR(uint64_t ivec, const char *src, char *dest, size_t dataLen){
size_t ncOff = 0;
unsigned char streamBlock[] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
key = binKey(key);
unsigned char nonceCtr[] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Bit::htobll((char *)nonceCtr, ivec);
return mbedtls_aes_crypt_ctr(&ctx, dataLen, &ncOff, nonceCtr, streamBlock,
(const unsigned char *)src, (unsigned char *)dest) == 0;
}
void verimatrixData::write(char *shmPage){
int offset = 0;
memcpy(shmPage + offset, url.c_str(), url.size() + 1);
offset += url.size() + 1; //+1 for the concluding 0-byte
memcpy(shmPage + offset, name.c_str(), name.size() + 1);
offset += name.size() + 1; //+1 for the concluding 0-byte
std::string tmpKey = hex(key);
memcpy(shmPage + offset, tmpKey.c_str(), tmpKey.size() + 1);
offset += tmpKey.size() + 1; //+1 for the concluding 0-byte
memcpy(shmPage + offset, keyid.c_str(), keyid.size() + 1);
offset += keyid.size() + 1; //+1 for the concluding 0-byte
memcpy(shmPage + offset, laurl.c_str(), laurl.size() + 1);
offset += laurl.size() + 1; //+1 for the concluding 0-byte
memcpy(shmPage + offset, lauurl.c_str(), lauurl.size() + 1);
DTSC::Packet AES::encryptPacketCBC(const DTSC::Meta &M, const DTSC::Packet &src, char *ivec, size_t newTrack){
DTSC::Packet res;
if (newTrack == INVALID_TRACK_ID){
FAIL_MSG("No target track given for track encryption!");
return res;
}
char *data;
size_t dataLen;
src.getString("data", data, dataLen);
size_t trackIdx = M.getSourceTrack(newTrack);
bool encrypt = false;
if (M.getCodec(trackIdx) == "H264"){
std::deque<nalu::nalData> nalUnits = h264::analysePackets(data, dataLen);
for (std::deque<nalu::nalData>::iterator it = nalUnits.begin(); it != nalUnits.end(); it++){
if (it->nalType != 1 && it->nalType != 5){continue;}
if (it->nalSize <= 48){continue;}
encrypt = true;
break;
}
}
if (!encrypt){
res.genericFill(src.getTime(), src.getInt("offset"), newTrack, data, dataLen, 0, src.getFlag("keyframe"));
return res;
}
char *encData = (char *)malloc(dataLen);
if (M.getCodec(trackIdx) == "H264"){
if (!encryptH264BlockFairplay(ivec, data, encData, dataLen)){
ERROR_MSG("Failed to encrypt a block of 16 bytes!");
free(encData);
return res;
}
}else{
INFO_MSG("Going to fully CBC encrypt a %s packet of %zu bytes", M.getType(trackIdx).c_str(), dataLen);
if (!encryptBlockCBC(ivec, data, encData, dataLen)){
FAIL_MSG("Failed to encrypt packet");
free(encData);
return res;
}
}
res.genericFill(src.getTime(), src.getInt("offset"), newTrack, encData, dataLen, 0, src.getFlag("keyframe"));
free(encData);
return res;
}
bool AES::encryptH264BlockFairplay(char *ivec, const char *src, char *dest, size_t dataLen){
size_t offset = 0;
std::deque<nalu::nalData> nalUnits = h264::analysePackets(src, dataLen);
for (std::deque<nalu::nalData>::iterator it = nalUnits.begin(); it != nalUnits.end(); it++){
if ((it->nalType != 1 && it->nalType != 5) || it->nalSize <= 48){
memcpy(dest + offset, src + offset, it->nalSize + 4);
offset += it->nalSize + 4;
continue;
}
memcpy(dest + offset, src + offset, 36);
offset += 36;
size_t encryptedBlocks = 0;
size_t lenToGo = it->nalSize - 32;
while (lenToGo){
if (lenToGo > 16){
if (!encryptBlockCBC(ivec, src + offset, dest + offset, 16)){
ERROR_MSG("Failed to encrypt a block of 16 bytes!");
return false;
}
offset += 16;
lenToGo -= 16;
++encryptedBlocks;
}
memcpy(dest + offset, src + offset, std::min(lenToGo, (size_t)144));
offset += std::min(lenToGo, (size_t)144);
lenToGo -= std::min(lenToGo, (size_t)144);
}
}
return true;
}
std::string AES::encryptBlockCBC(char *ivec, const std::string &inp){
char *resPtr = (char *)malloc(inp.size());
if (!encryptBlockCBC(ivec, inp.c_str(), resPtr, inp.size())){
free(resPtr);
return "";
}
std::string result(resPtr, inp.size());
free(resPtr);
return result;
}
bool AES::encryptBlockCBC(char *ivec, const char *src, char *dest, size_t dataLen){
if (dataLen % 16){WARN_MSG("Encrypting a non-multiple of 16 bytes: %zu", dataLen);}
return mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, dataLen, (unsigned char *)ivec,
(const unsigned char *)src, (unsigned char *)dest) == 0;
}
}// namespace Encryption

View file

@ -1,35 +1,28 @@
#pragma once
#include "dtsc.h"
#include <mbedtls/aes.h>
#include <string>
namespace Encryption{
class verimatrixData{
class AES{
public:
void read(const char *shmPage);
void write(char *shmPage);
std::string url;
std::string name;
std::string key;
std::string keyid;
std::string keyseed;
std::string laurl;
std::string lauurl;
AES();
~AES();
void setEncryptKey(const char *key);
void setDecryptKey(const char *key);
DTSC::Packet encryptPacketCTR(const DTSC::Meta &M, const DTSC::Packet &src, uint64_t ivec, size_t newTrack);
std::string encryptBlockCTR(uint64_t ivec, const std::string &inp);
bool encryptBlockCTR(uint64_t ivec, const char *src, char *dest, size_t dataLen);
bool encryptH264BlockFairplay(char *ivec, const char *src, char *dest, size_t dataLen);
DTSC::Packet encryptPacketCBC(const DTSC::Meta &M, const DTSC::Packet &src, char *ivec, size_t newTrack);
std::string encryptBlockCBC(char *ivec, const std::string &inp);
bool encryptBlockCBC(char *ivec, const char *src, char *dest, size_t dataLen);
protected:
mbedtls_aes_context ctx;
};
std::string hexString(const char *data, unsigned long dataLen);
std::string AES_Crypt(const std::string &data, const std::string &key, std::string &ivec);
std::string AES_Crypt(const char *data, int dataLen, const char *key, const char *ivec);
// These functions are dangerous for your data
void AESFullCrypt(char *data, int dataLen, const char *key, const char *ivec);
void AESPartialCrypt(char *data, int dataLen, char *expandedKey, char *eCount, char *iVec,
unsigned int &num, bool &initialize);
std::string PR_GenerateContentKey(std::string &keyseed, std::string &keyid);
std::string PR_GuidToByteArray(std::string &guid);
void encryptPlayReady(DTSC::Packet &pack, std::string &codec, const char *iVec, const char *key);
void fillVerimatrix(verimatrixData &vmData);
}// namespace Encryption

View file

@ -4,6 +4,7 @@
#include "adts.h"
#include "defines.h"
#include "flv_tag.h"
#include "mp4_generic.h"
#include "rtmpchunks.h"
#include "timing.h"
#include "util.h"
@ -323,17 +324,23 @@ FLV::Tag &FLV::Tag::operator=(const FLV::Tag &O){
return *this;
}// assignment operator
bool FLV::Tag::DTSCLoader(DTSC::Packet &packData, DTSC::Track &track){
bool FLV::Tag::DTSCLoader(DTSC::Packet &packData, const DTSC::Meta &M, size_t idx){
std::string meta_str;
len = 0;
if (track.type == "video"){
if (idx == INVALID_TRACK_ID){
WARN_MSG("packet with invalid track id found!");
return false;
}
std::string type = M.getType(idx);
std::string codec = M.getCodec(idx);
if (type == "video"){
char *tmpData = 0;
size_t tmpLen = 0;
packData.getString("data", tmpData, tmpLen);
len = tmpLen + 16;
if (track.codec == "H264"){len += 4;}
if (codec == "H264"){len += 4;}
if (!checkBufferSize()){return false;}
if (track.codec == "H264"){
if (codec == "H264"){
memcpy(data + 16, tmpData, len - 20);
data[12] = 1;
offset(packData.getInt("offset"));
@ -341,13 +348,13 @@ bool FLV::Tag::DTSCLoader(DTSC::Packet &packData, DTSC::Track &track){
memcpy(data + 12, tmpData, len - 16);
}
data[11] = 0;
if (track.codec == "H264"){data[11] |= 7;}
if (track.codec == "ScreenVideo2"){data[11] |= 6;}
if (track.codec == "VP6Alpha"){data[11] |= 5;}
if (track.codec == "VP6"){data[11] |= 4;}
if (track.codec == "ScreenVideo1"){data[11] |= 3;}
if (track.codec == "H263"){data[11] |= 2;}
if (track.codec == "JPEG"){data[11] |= 1;}
if (codec == "H264"){data[11] |= 7;}
if (codec == "ScreenVideo2"){data[11] |= 6;}
if (codec == "VP6Alpha"){data[11] |= 5;}
if (codec == "VP6"){data[11] |= 4;}
if (codec == "ScreenVideo1"){data[11] |= 3;}
if (codec == "H263"){data[11] |= 2;}
if (codec == "JPEG"){data[11] |= 1;}
if (packData.getFlag("keyframe")){
data[11] |= 0x10;
}else{
@ -355,32 +362,32 @@ bool FLV::Tag::DTSCLoader(DTSC::Packet &packData, DTSC::Track &track){
}
if (packData.getFlag("disposableframe")){data[11] |= 0x30;}
}
if (track.type == "audio"){
if (type == "audio"){
char *tmpData = 0;
size_t tmpLen = 0;
packData.getString("data", tmpData, tmpLen);
len = tmpLen + 16;
if (track.codec == "AAC"){len++;}
if (codec == "AAC"){len++;}
if (!checkBufferSize()){return false;}
if (track.codec == "AAC"){
if (codec == "AAC"){
memcpy(data + 13, tmpData, len - 17);
data[12] = 1; // raw AAC data, not sequence header
}else{
memcpy(data + 12, tmpData, len - 16);
}
unsigned int datarate = track.rate;
unsigned int datarate = M.getRate(idx);
data[11] = 0;
if (track.codec == "AAC"){data[11] |= 0xA0;}
if (track.codec == "MP3"){
if (codec == "AAC"){data[11] |= 0xA0;}
if (codec == "MP3"){
if (datarate == 8000){
data[11] |= 0xE0;
}else{
data[11] |= 0x20;
}
}
if (track.codec == "ADPCM"){data[11] |= 0x10;}
if (track.codec == "PCM"){data[11] |= 0x30;}
if (track.codec == "Nellymoser"){
if (codec == "ADPCM"){data[11] |= 0x10;}
if (codec == "PCM"){data[11] |= 0x30;}
if (codec == "Nellymoser"){
if (datarate == 8000){
data[11] |= 0x50;
}else if (datarate == 16000){
@ -389,9 +396,9 @@ bool FLV::Tag::DTSCLoader(DTSC::Packet &packData, DTSC::Track &track){
data[11] |= 0x60;
}
}
if (track.codec == "ALAW"){data[11] |= 0x70;}
if (track.codec == "ULAW"){data[11] |= 0x80;}
if (track.codec == "Speex"){data[11] |= 0xB0;}
if (codec == "ALAW"){data[11] |= 0x70;}
if (codec == "ULAW"){data[11] |= 0x80;}
if (codec == "Speex"){data[11] |= 0xB0;}
if (datarate >= 44100){
data[11] |= 0x0C;
}else if (datarate >= 22050){
@ -399,14 +406,14 @@ bool FLV::Tag::DTSCLoader(DTSC::Packet &packData, DTSC::Track &track){
}else if (datarate >= 11025){
data[11] |= 0x04;
}
if (track.size != 8){data[11] |= 0x02;}
if (track.channels > 1){data[11] |= 0x01;}
if (M.getSize(idx) != 8){data[11] |= 0x02;}
if (M.getChannels(idx) > 1){data[11] |= 0x01;}
}
if (!len){return false;}
setLen();
if (track.type == "video"){data[0] = 0x09;}
if (track.type == "audio"){data[0] = 0x08;}
if (track.type == "meta"){data[0] = 0x12;}
if (type == "video"){data[0] = 0x09;}
if (type == "audio"){data[0] = 0x08;}
if (type == "meta"){data[0] = 0x12;}
data[1] = ((len - 15) >> 16) & 0xFF;
data[2] = ((len - 15) >> 8) & 0xFF;
data[3] = (len - 15) & 0xFF;
@ -431,13 +438,14 @@ void FLV::Tag::setLen(){
}
/// FLV Video init data loader function from metadata.
bool FLV::Tag::DTSCVideoInit(DTSC::Track &video){
bool FLV::Tag::DTSCVideoInit(DTSC::Meta &meta, uint32_t vTrack){
// Unknown? Assume H264.
len = 0;
if (video.codec == "?"){video.codec = "H264";}
if (video.codec == "H264"){len = video.init.size() + 20;}
if (meta.getCodec(vTrack) == "?"){meta.setCodec(vTrack, "H264");}
std::string initData = meta.getInit(vTrack);
if (meta.getCodec(vTrack) == "H264"){len = initData.size() + 20;}
if (len <= 0 || !checkBufferSize()){return false;}
memcpy(data + 16, video.init.c_str(), len - 20);
memcpy(data + 16, initData.c_str(), len - 20);
data[12] = 0; // H264 sequence header
data[13] = 0;
data[14] = 0;
@ -456,18 +464,19 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Track &video){
}
/// FLV Audio init data loader function from metadata.
bool FLV::Tag::DTSCAudioInit(DTSC::Track &audio){
bool FLV::Tag::DTSCAudioInit(DTSC::Meta &meta, uint32_t aTrack){
len = 0;
// Unknown? Assume AAC.
if (audio.codec == "?"){audio.codec = "AAC";}
if (audio.codec == "AAC"){len = audio.init.size() + 17;}
if (meta.getCodec(aTrack) == "?"){meta.setCodec(aTrack, "AAC");}
std::string initData = meta.getInit(aTrack);
if (meta.getCodec(aTrack) == "AAC"){len = initData.size() + 17;}
if (len <= 0 || !checkBufferSize()){return false;}
memcpy(data + 13, audio.init.c_str(), len - 17);
memcpy(data + 13, initData.c_str(), len - 17);
data[12] = 0; // AAC sequence header
data[11] = 0;
if (audio.codec == "AAC"){data[11] += 0xA0;}
if (audio.codec == "MP3"){data[11] += 0x20;}
unsigned int datarate = audio.rate;
if (meta.getCodec(aTrack) == "AAC"){data[11] += 0xA0;}
if (meta.getCodec(aTrack) == "MP3"){data[11] += 0x20;}
unsigned int datarate = meta.getRate(aTrack);
if (datarate >= 44100){
data[11] += 0x0C;
}else if (datarate >= 22050){
@ -475,8 +484,8 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Track &audio){
}else if (datarate >= 11025){
data[11] += 0x04;
}
if (audio.size != 8){data[11] += 0x02;}
if (audio.channels > 1){data[11] += 0x01;}
if (meta.getSize(aTrack) != 8){data[11] += 0x02;}
if (meta.getChannels(aTrack) > 1){data[11] += 0x01;}
setLen();
data[0] = 0x08;
data[1] = ((len - 15) >> 16) & 0xFF;
@ -489,86 +498,87 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Track &audio){
return true;
}
bool FLV::Tag::DTSCMetaInit(DTSC::Meta &M, std::set<long unsigned int> &selTracks){
bool FLV::Tag::DTSCMetaInit(const DTSC::Meta &M, std::set<long unsigned int> &selTracks){
AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER);
amfdata.addContent(AMF::Object("", "onMetaData"));
amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY));
AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY);
int i = 0;
unsigned long long mediaLen = 0;
uint64_t mediaLen = 0;
for (std::set<long unsigned int>::iterator it = selTracks.begin(); it != selTracks.end(); it++){
if (M.tracks[*it].lastms - M.tracks[*it].firstms > mediaLen){
mediaLen = M.tracks[*it].lastms - M.tracks[*it].firstms;
if (M.getLastms(*it) - M.getFirstms(*it) > mediaLen){
mediaLen = M.getLastms(*it) - M.getFirstms(*it);
}
if (M.tracks[*it].type == "video"){
if (M.getType(*it) == "video"){
trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT));
trinfo.getContentP(i)->addContent(AMF::Object(
"length", ((double)M.tracks[*it].lastms / 1000) * ((double)M.tracks[*it].fpks / 1000.0), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(
AMF::Object("timescale", ((double)M.tracks[*it].fpks / 1000.0), AMF::AMF0_NUMBER));
"length", ((double)M.getLastms(*it) / 1000) * ((double)M.getFpks(*it) / 1000.0), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)M.getFpks(*it) / 1000), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY));
amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL));
if (M.tracks[*it].codec == "H264"){
std::string codec = M.getCodec(*it);
if (codec == "H264"){
amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "avc1"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "avc1"));
}
if (M.tracks[*it].codec == "ScreenVideo2"){
if (codec == "ScreenVideo2"){
amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 6, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "sv2"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "sv2"));
}
if (M.tracks[*it].codec == "VP6Alpha"){
if (codec == "VP6Alpha"){
amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 5, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "vp6a"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "vp6a"));
}
if (M.tracks[*it].codec == "VP6"){
if (codec == "VP6"){
amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "vp6"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "vp6"));
}
if (M.tracks[*it].codec == "ScreenVideo1"){
if (codec == "ScreenVideo1"){
amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 3, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "sv1"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "sv1"));
}
if (M.tracks[*it].codec == "H263"){
if (codec == "H263"){
amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "h263"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "h263"));
}
if (M.tracks[*it].codec == "JPEG"){
if (codec == "JPEG"){
amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 1, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "jpeg"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "jpeg"));
}
amfdata.getContentP(1)->addContent(AMF::Object("width", M.tracks[*it].width, AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("height", M.tracks[*it].height, AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("width", M.getWidth(*it), AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("height", M.getHeight(*it), AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(
AMF::Object("videoframerate", (double)M.tracks[*it].fpks / 1000.0, AMF::AMF0_NUMBER));
AMF::Object("videoframerate", (double)M.getFpks(*it) / 1000.0, AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(
AMF::Object("videodatarate", (double)M.tracks[*it].bps / 128.0, AMF::AMF0_NUMBER));
AMF::Object("videodatarate", (double)M.getBps(*it) / 128.0, AMF::AMF0_NUMBER));
++i;
}
if (M.tracks[*it].type == "audio"){
if (M.getType(*it) == "audio"){
trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT));
trinfo.getContentP(i)->addContent(AMF::Object(
"length", ((double)M.tracks[*it].lastms) * ((double)M.tracks[*it].rate), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("timescale", M.tracks[*it].rate, AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(
AMF::Object("length", (double)(M.getLastms(*it) * M.getRate(*it)), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("timescale", M.getRate(*it), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY));
amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL));
amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0.0, AMF::AMF0_NUMBER));
if (M.tracks[*it].codec == "AAC"){
amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string) "mp4a"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "mp4a"));
std::string codec = M.getCodec(*it);
if (codec == "AAC"){
amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", "mp4a"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "mp4a"));
}
if (M.tracks[*it].codec == "MP3"){
amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string) "mp3"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string) "mp3"));
if (codec == "MP3"){
amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", "mp3"));
trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", "mp3"));
}
amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", M.tracks[*it].channels, AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", M.tracks[*it].rate, AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", M.tracks[*it].size, AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", M.getChannels(*it), AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", M.getRate(*it), AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", M.getSize(*it), AMF::AMF0_NUMBER));
amfdata.getContentP(1)->addContent(
AMF::Object("audiodatarate", (double)M.tracks[*it].bps / 128.0, AMF::AMF0_NUMBER));
AMF::Object("audiodatarate", (double)M.getBps(*it) / 128.0, AMF::AMF0_NUMBER));
++i;
}
}
if (M.vod){
if (M.getVod()){
amfdata.getContentP(1)->addContent(AMF::Object("duration", mediaLen / 1000, AMF::AMF0_NUMBER));
}
amfdata.getContentP(1)->addContent(trinfo);
@ -742,8 +752,8 @@ bool FLV::Tag::FileLoader(FILE *f){
}else{
// if a tag header, calculate length and read tag body
len = data[3] + 15;
len += (data[2] << 8);
len += (data[1] << 16);
len += (((unsigned int)data[2]) << 8);
len += (((unsigned int)data[1]) << 16);
if (!checkBufferSize()){return false;}
if (data[0] > 0x12){
data[0] += 32;
@ -781,7 +791,7 @@ unsigned int FLV::Tag::getTrackID(){
case 0x08: return 2; // audio track
case 0x09: return 1; // video track
case 0x12: return 3; // meta track
default: return 0;
default: return INVALID_TRACK_ID;
}
}
@ -806,13 +816,16 @@ unsigned int FLV::Tag::getDataLen(){
return len - 16;
}
void FLV::Tag::toMeta(DTSC::Meta &metadata, AMF::Object &amf_storage, unsigned int reTrack){
if (!reTrack){
switch (data[0]){
case 0x09: reTrack = 1; break; // video
case 0x08: reTrack = 2; break; // audio
case 0x12: reTrack = 3; break; // meta
}
void FLV::Tag::toMeta(DTSC::Meta &meta, AMF::Object &amf_storage){
size_t reTrack = INVALID_TRACK_ID;
toMeta(meta, amf_storage, reTrack);
}
void FLV::Tag::toMeta(DTSC::Meta &meta, AMF::Object &amf_storage, size_t &reTrack){
std::string trackType;
switch (data[0]){
case 0x09: trackType = "video"; break; // video
case 0x08: trackType = "audio"; break; // audio
case 0x12: trackType = "meta"; break; // meta
}
if (data[0] == 0x12){
@ -828,45 +841,44 @@ void FLV::Tag::toMeta(DTSC::Meta &metadata, AMF::Object &amf_storage, unsigned i
if (tmp){amf_storage = *tmp;}
return;
}
if (data[0] == 0x08 && (metadata.tracks[reTrack].codec == "" || metadata.tracks[reTrack].codec != getAudioCodec() ||
(needsInitData() && isInitData()))){
if (meta.getVod() && reTrack == INVALID_TRACK_ID){
reTrack = meta.trackIDToIndex(getTrackID(), getpid());
}
if (reTrack == INVALID_TRACK_ID){
reTrack = meta.addTrack();
meta.setID(reTrack, getTrackID());
}
std::string codec = meta.getCodec(reTrack);
if (data[0] == 0x08 && (codec == "" || codec != getAudioCodec() || (needsInitData() && isInitData()))){
char audiodata = data[11];
metadata.tracks[reTrack].trackID = reTrack;
metadata.tracks[reTrack].type = "audio";
metadata.tracks[reTrack].codec = getAudioCodec();
meta.setType(reTrack, "audio");
meta.setCodec(reTrack, getAudioCodec());
switch (audiodata & 0x0C){
case 0x0: metadata.tracks[reTrack].rate = 5512; break;
case 0x4: metadata.tracks[reTrack].rate = 11025; break;
case 0x8: metadata.tracks[reTrack].rate = 22050; break;
case 0xC: metadata.tracks[reTrack].rate = 44100; break;
case 0x0: meta.setRate(reTrack, 5512); break;
case 0x4: meta.setRate(reTrack, 11025); break;
case 0x8: meta.setRate(reTrack, 22050); break;
case 0xC: meta.setRate(reTrack, 44100); break;
}
if (amf_storage.getContentP("audiosamplerate")){
metadata.tracks[reTrack].rate = (long long int)amf_storage.getContentP("audiosamplerate")->NumValue();
}
switch (audiodata & 0x02){
case 0x0: metadata.tracks[reTrack].size = 8; break;
case 0x2: metadata.tracks[reTrack].size = 16; break;
meta.setRate(reTrack, amf_storage.getContentP("audiosamplerate")->NumValue());
}
meta.setSize(reTrack, audiodata & 0x02 ? 16 : 8);
if (amf_storage.getContentP("audiosamplesize")){
metadata.tracks[reTrack].size = (long long int)amf_storage.getContentP("audiosamplesize")->NumValue();
}
switch (audiodata & 0x01){
case 0x0: metadata.tracks[reTrack].channels = 1; break;
case 0x1: metadata.tracks[reTrack].channels = 2; break;
meta.setSize(reTrack, amf_storage.getContentP("audiosamplesize")->NumValue());
}
meta.setChannels(reTrack, audiodata & 0x01 ? 2 : 1);
if (amf_storage.getContentP("stereo")){
if (amf_storage.getContentP("stereo")->NumValue() == 1){
metadata.tracks[reTrack].channels = 2;
}else{
metadata.tracks[reTrack].channels = 1;
}
meta.setChannels(reTrack, amf_storage.getContentP("stereo")->NumValue() == 1 ? 2 : 1);
}
if (needsInitData() && isInitData()){
if ((audiodata & 0xF0) == 0xA0){
metadata.tracks[reTrack].init = std::string((char *)data + 13, (size_t)len - 17);
meta.setInit(reTrack, data + 13, len - 17);
}else{
metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16);
meta.setInit(reTrack, data + 12, len - 16);
}
if (metadata.tracks[reTrack].codec == "AAC"){
metadata.tracks[reTrack].rate = aac::AudSpecConf::rate(metadata.tracks[reTrack].init);
@ -875,44 +887,47 @@ void FLV::Tag::toMeta(DTSC::Meta &metadata, AMF::Object &amf_storage, unsigned i
}
}
if (data[0] == 0x09 && ((needsInitData() && isInitData()) || !metadata.tracks[reTrack].codec.size())){
if (data[0] == 0x09 && ((needsInitData() && isInitData()) || !codec.size())){
char videodata = data[11];
metadata.tracks[reTrack].codec = getVideoCodec();
metadata.tracks[reTrack].type = "video";
metadata.tracks[reTrack].trackID = reTrack;
meta.setCodec(reTrack, getVideoCodec());
meta.setType(reTrack, "video");
if (amf_storage.getContentP("width")){
metadata.tracks[reTrack].width = (long long int)amf_storage.getContentP("width")->NumValue();
meta.setWidth(reTrack, amf_storage.getContentP("width")->NumValue());
}
if (amf_storage.getContentP("height")){
metadata.tracks[reTrack].height = (long long int)amf_storage.getContentP("height")->NumValue();
meta.setHeight(reTrack, amf_storage.getContentP("height")->NumValue());
}
if (!metadata.tracks[reTrack].fpks && amf_storage.getContentP("videoframerate")){
if (!meta.getFpks(reTrack) && amf_storage.getContentP("videoframerate")){
if (amf_storage.getContentP("videoframerate")->NumValue()){
metadata.tracks[reTrack].fpks =
(long long int)(amf_storage.getContentP("videoframerate")->NumValue() * 1000.0);
meta.setFpks(reTrack, amf_storage.getContentP("videoframerate")->NumValue() * 1000.0);
}else{
metadata.tracks[reTrack].fpks =
atoi(amf_storage.getContentP("videoframerate")->StrValue().c_str()) * 1000.0;
meta.setFpks(reTrack, atoi(amf_storage.getContentP("videoframerate")->StrValue().c_str()) * 1000.0);
}
}
if (needsInitData() && isInitData()){
if ((videodata & 0x0F) == 7){
if (len < 21){return;}
metadata.tracks[reTrack].init = std::string((char *)data + 16, (size_t)len - 20);
MP4::AVCC avccBox;
avccBox.setPayload(data + 16, len - 20);
avccBox.sanitize();
meta.setInit(reTrack, avccBox.payload(), avccBox.payloadSize());
}else{
if (len < 17){return;}
metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16);
MP4::AVCC avccBox;
avccBox.setPayload(data + 12, len - 16);
avccBox.sanitize();
meta.setInit(reTrack, avccBox.payload(), avccBox.payloadSize());
}
/// this is a hacky way around invalid FLV data (since it gets ignored nearly everywhere, but
/// we do need correct data...
if (!metadata.tracks[reTrack].width || !metadata.tracks[reTrack].height ||
!metadata.tracks[reTrack].fpks){
if (!meta.getWidth(reTrack) || !meta.getHeight(reTrack) || !meta.getFpks(reTrack)){
std::string init = meta.getInit(reTrack);
h264::sequenceParameterSet sps;
sps.fromDTSCInit(metadata.tracks[reTrack].init);
sps.fromDTSCInit(init);
h264::SPSMeta spsChar = sps.getCharacteristics();
metadata.tracks[reTrack].width = spsChar.width;
metadata.tracks[reTrack].height = spsChar.height;
metadata.tracks[reTrack].fpks = spsChar.fps * 1000;
meta.setWidth(reTrack, spsChar.width);
meta.setHeight(reTrack, spsChar.height);
meta.setFpks(reTrack, spsChar.fps * 1000);
}
}
}

View file

@ -49,11 +49,12 @@ namespace FLV{
~Tag(); ///< Generic destructor.
// loader functions
bool ChunkLoader(const RTMPStream::Chunk &O);
bool DTSCLoader(DTSC::Packet &packData, DTSC::Track &track);
bool DTSCVideoInit(DTSC::Track &video);
bool DTSCAudioInit(DTSC::Track &audio);
bool DTSCMetaInit(DTSC::Meta &M, std::set<long unsigned int> &selTracks);
void toMeta(DTSC::Meta &metadata, AMF::Object &amf_storage, unsigned int reTrack = 0);
bool DTSCLoader(DTSC::Packet &packData, const DTSC::Meta &M, size_t idx);
bool DTSCVideoInit(DTSC::Meta &meta, uint32_t vTrack);
bool DTSCAudioInit(DTSC::Meta &meta, uint32_t aTrack);
bool DTSCMetaInit(const DTSC::Meta &M, std::set<long unsigned int> &selTracks);
void toMeta(DTSC::Meta &meta, AMF::Object &amf_storage);
void toMeta(DTSC::Meta &meta, AMF::Object &amf_storage, size_t &reTrack);
bool MemLoader(char *D, unsigned int S, unsigned int &P);
bool FileLoader(FILE *f);
unsigned int getTrackID();

View file

@ -1039,7 +1039,6 @@ namespace h264{
if (videoSignalTypePresentFlag){
videoFormat = bs.get(3);
videoFullRangeFlag = bs.get(1);
;
colourDescriptionPresentFlag = bs.get(1);
if (colourDescriptionPresentFlag){
colourPrimaries = bs.get(8);

View file

@ -44,7 +44,6 @@ namespace h264{
uint32_t chroma_format_idc; ///< the value of chroma_format_idc
std::string MyData; ///< The h264 nal unit data
};
// NAL class
/// Special instance of NAL class for analyzing SPS nal units
class SPS : public NAL{
@ -96,7 +95,6 @@ namespace h264{
virtual void setSPSNumber(size_t newNumber){}
virtual void setPPSNumber(size_t newNumber){}
protected:
std::string payload;
};

View file

@ -509,8 +509,7 @@ namespace h265{
profileTierLevel(bs, maxSubLayersMinus1, res);
bs.getUExpGolomb();
uint64_t chromaFormatIdc = bs.getUExpGolomb();
bool separateColorPlane = false;
if (chromaFormatIdc == 3){separateColorPlane = bs.get(1);}
if (chromaFormatIdc == 3){bs.skip(1);}
res.width = bs.getUExpGolomb();
res.height = bs.getUExpGolomb();
bool conformanceWindow = bs.get(1);

View file

@ -275,7 +275,8 @@ void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::C
void HTTP::Parser::StartResponse(std::string code, std::string message, HTTP::Parser &request,
Socket::Connection &conn, bool bufferAllChunks){
std::string prot = request.protocol;
sendingChunks = (!bufferAllChunks && protocol == "HTTP/1.1" && request.GetHeader("Connection") != "close");
sendingChunks =
(!bufferAllChunks && request.protocol == "HTTP/1.1" && request.GetHeader("Connection") != "close");
CleanPreserveHeaders();
protocol = prot;
if (sendingChunks){

View file

@ -1,5 +1,4 @@
/// \file json.h Holds all JSON-related headers.
#pragma once
#include "socket.h"
#include <deque>

View file

@ -80,6 +80,7 @@ namespace MP4{
class fullBox : public Box{
public:
fullBox();
fullBox(const Box &rs) : Box(rs){}
void setVersion(char newVersion);
char getVersion() const;
void setFlags(uint32_t newFlags);

View file

@ -4,9 +4,10 @@
#include "mp4_generic.h"
namespace MP4{
MFHD::MFHD(){
MFHD::MFHD(uint32_t sequenceNumber){
memcpy(data + 4, "mfhd", 4);
setInt32(0, 0);
setSequenceNumber(sequenceNumber);
}
void MFHD::setSequenceNumber(uint32_t newSequenceNumber){setInt32(newSequenceNumber, 4);}
@ -1381,7 +1382,9 @@ namespace MP4{
}
// Note: next 4 headers inherit from fullBox, start at byte 4.
VMHD::VMHD(){
VMHD::VMHD(uint32_t version, uint32_t flags){
setVersion(version);
setFlags(flags);
memcpy(data + 4, "vmhd", 4);
setGraphicsMode(0);
setOpColor(0, 0);
@ -1563,7 +1566,7 @@ namespace MP4{
uint32_t offset = 8; // start of boxes
for (i = 0; i < getEntryCount() && i < index; i++){offset += getBoxLen(offset);}
if (index + 1 > getEntryCount()){
int amount = index + 1 - getEntryCount();
int amount = index - getEntryCount();
if (!reserve(payloadOffset + offset, 0, amount * 8)){return;}
for (int j = 0; j < amount; ++j){
memcpy(data + payloadOffset + offset + j * 8, "\000\000\000\010erro", 8);
@ -1922,14 +1925,14 @@ namespace MP4{
setHeight(height);
}
TKHD::TKHD(DTSC::Track &track, bool fragmented){
TKHD::TKHD(const DTSC::Meta &M, size_t idx){
initialize();
setTrackID(track.trackID);
setTrackID(idx + 1);
setDuration(-1);
if (!fragmented){setDuration(track.lastms - track.firstms);}
if (track.type == "video"){
setWidth(track.width);
setHeight(track.height);
if (M.getVod()){setDuration(M.getLastms(idx) - M.getFirstms(idx));}
if (M.getType(idx) == "video"){
setWidth(M.getWidth(idx));
setHeight(M.getHeight(idx));
}
}
@ -2136,7 +2139,7 @@ namespace MP4{
return r.str();
}
MDHD::MDHD(uint64_t duration){
MDHD::MDHD(uint64_t duration, const std::string &language){
memcpy(data + 4, "mdhd", 4);
// reserve an entire version 0 box
if (!reserve(0, 9, 32)){
@ -2146,6 +2149,7 @@ namespace MP4{
setTimeScale(1000);
setDuration(duration);
setLanguage(language);
}
void MDHD::setCreationTime(uint64_t newCreationTime){
@ -2643,22 +2647,23 @@ namespace MP4{
VisualSampleEntry::VisualSampleEntry(){initialize();}
VisualSampleEntry::VisualSampleEntry(DTSC::Track &track){
VisualSampleEntry::VisualSampleEntry(const DTSC::Meta &M, size_t idx){
std::string tCodec = M.getCodec(idx);
initialize();
setDataReferenceIndex(1);
setWidth(track.width);
setHeight(track.height);
if (track.codec == "H264"){
setWidth(M.getWidth(idx));
setHeight(M.getHeight(idx));
if (tCodec == "H264"){
setCodec("avc1");
MP4::AVCC avccBox;
avccBox.setPayload(track.init);
avccBox.setPayload(M.getInit(idx));
setCLAP(avccBox);
}
/*LTS-START*/
if (track.codec == "HEVC"){
if (tCodec == "HEVC"){
setCodec("hev1");
MP4::HVCC hvccBox;
hvccBox.setPayload(track.init);
hvccBox.setPayload(M.getInit(idx));
setCLAP(hvccBox);
}
/*LTS-END*/
@ -2678,6 +2683,8 @@ namespace MP4{
void VisualSampleEntry::setCodec(const char *newCodec){memcpy(data + 4, newCodec, 4);}
std::string VisualSampleEntry::getCodec(){return std::string(data + 4, 4);}
void VisualSampleEntry::setWidth(uint16_t newWidth){setInt16(newWidth, 24);}
uint16_t VisualSampleEntry::getWidth(){return getInt16(24);}
@ -2815,19 +2822,20 @@ namespace MP4{
AudioSampleEntry::AudioSampleEntry(){initialize();}
AudioSampleEntry::AudioSampleEntry(DTSC::Track &track){
AudioSampleEntry::AudioSampleEntry(const DTSC::Meta &M, size_t idx){
std::string tCodec = M.getCodec(idx);
initialize();
if (track.codec == "AAC" || track.codec == "MP3"){setCodec("mp4a");}
if (track.codec == "AC3"){setCodec("ac-3");}
if (tCodec == "AAC" || tCodec == "MP3"){setCodec("mp4a");}
if (tCodec == "AC3"){setCodec("ac-3");}
setDataReferenceIndex(1);
setSampleRate(track.rate);
setChannelCount(track.channels);
setSampleSize(track.size);
if (track.codec == "AC3"){
MP4::DAC3 dac3Box(track.rate, track.channels);
setSampleRate(M.getRate(idx));
setChannelCount(M.getChannels(idx));
setSampleSize(M.getSize(idx));
if (tCodec == "AC3"){
MP4::DAC3 dac3Box(M.getRate(idx), M.getChannels(idx));
setCodecBox(dac3Box);
}else{// other codecs use the ESDS box
MP4::ESDS esdsBox(track.init);
MP4::ESDS esdsBox(M.getInit(idx));
setCodecBox(esdsBox);
}
}
@ -2938,14 +2946,14 @@ namespace MP4{
return r.str();
}
TextSampleEntry::TextSampleEntry(DTSC::Track &track){
TextSampleEntry::TextSampleEntry(const DTSC::Meta &M, size_t idx){
initialize();
if (track.codec == "subtitle"){
if (M.getCodec(idx) == "subtitle"){
setCodec("tx3g");
}else{
// not supported codec
INFO_MSG("not supported codec: %s", track.codec.c_str());
INFO_MSG("not supported codec: %s", M.getCodec(idx).c_str());
}
}
@ -3258,7 +3266,11 @@ namespace MP4{
return toPrettyCFBString(indent, "[meta] Meta Box");
}
ELST::ELST(){memcpy(data + 4, "elst", 4);}
ELST::ELST(){
memcpy(data + 4, "elst", 4);
setVersion(0);
setFlags(0);
}
void ELST::setCount(uint32_t newVal){setInt32(newVal, 4);}

View file

@ -8,7 +8,7 @@ namespace h265{
namespace MP4{
class MFHD : public Box{
public:
MFHD();
MFHD(uint32_t sequenceNumber = 0);
void setSequenceNumber(uint32_t newSequenceNumber);
uint32_t getSequenceNumber();
std::string toPrettyString(uint32_t indent = 0);
@ -357,7 +357,7 @@ namespace MP4{
class VMHD : public fullBox{
public:
VMHD();
VMHD(uint32_t version = 0, uint32_t flags = 0);
void setGraphicsMode(uint16_t newGraphicsMode);
uint16_t getGraphicsMode();
uint32_t getOpColorCount();
@ -492,8 +492,9 @@ namespace MP4{
class TKHD : public fullBox{
public:
TKHD(const Box &rs) : fullBox(rs){}
TKHD(uint32_t trackId = 0, uint64_t duration = 0, uint32_t width = 0, uint32_t height = 0);
TKHD(DTSC::Track &track, bool fragmented);
TKHD(const DTSC::Meta &M, size_t idx);
void setCreationTime(uint64_t newCreationTime);
uint64_t getCreationTime();
@ -527,7 +528,7 @@ namespace MP4{
class MDHD : public fullBox{
public:
MDHD(uint64_t duration = 0);
MDHD(uint64_t duration = 0, const std::string &language = "");
void setCreationTime(uint64_t newCreationTime);
uint64_t getCreationTime();
void setModificationTime(uint64_t newModificationTime);
@ -593,6 +594,7 @@ namespace MP4{
class STCO : public fullBox{
public:
STCO(const Box &rs) : fullBox(rs){}
STCO(char v = 1, uint32_t f = 0);
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
@ -604,6 +606,7 @@ namespace MP4{
class CO64 : public fullBox{
public:
CO64(char v = 1, uint32_t f = 0);
CO64(const Box &rs) : fullBox(rs){}
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
void setChunkOffset(uint64_t newChunkOffset, uint32_t no);
@ -667,9 +670,10 @@ namespace MP4{
///\todo set default values
public:
VisualSampleEntry();
VisualSampleEntry(DTSC::Track &track);
VisualSampleEntry(const DTSC::Meta &M, size_t idx);
void initialize();
void setCodec(const char *newCodec);
std::string getCodec();
void setWidth(uint16_t newWidth);
uint16_t getWidth();
void setHeight(uint16_t newHeight);
@ -700,7 +704,7 @@ namespace MP4{
public:
///\todo set default values
AudioSampleEntry();
AudioSampleEntry(DTSC::Track &track);
AudioSampleEntry(const DTSC::Meta &M, size_t idx);
void initialize();
void setCodec(const char *newCodec);
void setChannelCount(uint16_t newChannelCount);
@ -761,7 +765,7 @@ namespace MP4{
class TextSampleEntry : public SampleEntry{
public:
TextSampleEntry();
TextSampleEntry(DTSC::Track &track);
TextSampleEntry(const DTSC::Meta &m, size_t idx);
void initialize();
void setHzJustification(int8_t n);
void setVtJustification(int8_t n);

View file

@ -13,7 +13,7 @@ namespace Mpeg{
// samplerate is encoded in bits 0x0C of header[2];
res.sampleRate = sampleRates[mpegVersion][((hdr[2] >> 2) & 0x03)] * 1000;
res.channels = 2 - (hdr[3] >> 7);
res.layer = 4 - (hdr[1] >> 1) & 0x03;
res.layer = 4 - ((hdr[1] >> 1) & 0x03);
return res;
}

View file

@ -25,36 +25,36 @@ namespace RTP{
char *Packet::getPayload() const{return data + getHsize();}
unsigned int Packet::getVersion() const{return (data[0] >> 6) & 0x3;}
uint32_t Packet::getVersion() const{return (data[0] >> 6) & 0x3;}
unsigned int Packet::getPadding() const{return (data[0] >> 5) & 0x1;}
uint32_t Packet::getPadding() const{return (data[0] >> 5) & 0x1;}
unsigned int Packet::getExtension() const{return (data[0] >> 4) & 0x1;}
uint32_t Packet::getExtension() const{return (data[0] >> 4) & 0x1;}
unsigned int Packet::getContribCount() const{return (data[0]) & 0xE;}
uint32_t Packet::getContribCount() const{return (data[0]) & 0xE;}
unsigned int Packet::getMarker() const{return (data[1] >> 7) & 0x1;}
uint32_t Packet::getMarker() const{return (data[1] >> 7) & 0x1;}
unsigned int Packet::getPayloadType() const{return (data[1]) & 0x7F;}
uint32_t Packet::getPayloadType() const{return (data[1]) & 0x7F;}
unsigned int Packet::getSequence() const{return (((((unsigned int)data[2]) << 8) + data[3]));}
uint16_t Packet::getSequence() const{return Bit::btohs(data + 2);}
uint32_t Packet::getTimeStamp() const{return Bit::btohl(data + 4);}
unsigned int Packet::getSSRC() const{return ntohl(*((unsigned int *)(data + 8)));}
unsigned int Packet::getSSRC() const{return Bit::btohl(data + 8);}
char *Packet::getData(){return data + 8 + 4 * getContribCount() + getExtension();}
const char *Packet::getData(){return data + 8 + 4 * getContribCount() + getExtension();}
void Packet::setTimestamp(uint32_t t){Bit::htobl(data + 4, t);}
void Packet::setTimestamp(uint32_t timestamp){Bit::htobl(data + 4, timestamp);}
void Packet::setSequence(unsigned int seq){*((short *)(data + 2)) = htons(seq);}
void Packet::setSequence(uint16_t seq){Bit::htobs(data + 2, seq);}
void Packet::setSSRC(unsigned long ssrc){*((int *)(data + 8)) = htonl(ssrc);}
void Packet::setSSRC(uint32_t ssrc){Bit::htobl(data + 8, ssrc);}
void Packet::increaseSequence(){*((short *)(data + 2)) = htons(getSequence() + 1);}
void Packet::increaseSequence(){setSequence(getSequence() + 1);}
void Packet::sendH264(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel, bool lastOfAccesUnit){
void Packet::sendH264(void *socket, void callBack(void *, const char *, size_t, uint8_t),
const char *payload, uint32_t payloadlen, uint32_t channel, bool lastOfAccesUnit){
if ((payload[0] & 0x1F) == 12){return;}
/// \todo This function probably belongs in DMS somewhere.
if (payloadlen + getHsize() + 2 <= maxDataLen){
@ -102,7 +102,7 @@ namespace RTP{
}
}
void Packet::sendVP8(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
void Packet::sendVP8(void *socket, void callBack(void *, const char *, size_t, uint8_t),
const char *payload, unsigned int payloadlen, unsigned int channel){
bool isKeyframe = ((payload[0] & 0x01) == 0) ? true : false;
@ -133,7 +133,7 @@ namespace RTP{
// WARN_MSG("KEYFRAME: %c", (isKeyframe) ? 'y' : 'n');
}
void Packet::sendH265(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
void Packet::sendH265(void *socket, void callBack(void *, const char *, size_t, uint8_t),
const char *payload, unsigned int payloadlen, unsigned int channel){
/// \todo This function probably belongs in DMS somewhere.
if (payloadlen + getHsize() + 3 <= maxDataLen){
@ -175,7 +175,7 @@ namespace RTP{
}
}
void Packet::sendMPEG2(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
void Packet::sendMPEG2(void *socket, void callBack(void *, const char *, size_t, uint8_t),
const char *payload, unsigned int payloadlen, unsigned int channel){
/// \todo This function probably belongs in DMS somewhere.
if (payloadlen + getHsize() + 4 <= maxDataLen){
@ -223,8 +223,8 @@ namespace RTP{
}
}
void Packet::sendData(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel, std::string codec){
void Packet::sendData(void *socket, void callBack(void *, const char *, size_t, uint8_t), const char *payload,
unsigned int payloadlen, unsigned int channel, std::string codec){
if (codec == "H264"){
unsigned long sent = 0;
while (sent < payloadlen){
@ -254,18 +254,18 @@ namespace RTP{
}
/// \todo This function probably belongs in DMS somewhere.
data[1] |= 0x80; // setting the RTP marker bit to 1
long offsetLen = 0;
size_t offsetLen = 0;
if (codec == "AAC"){
*((long *)(data + getHsize())) = htonl(((payloadlen << 3) & 0x0010fff8) | 0x00100000);
Bit::htobl(data + getHsize(), ((payloadlen << 3) & 0x0010fff8) | 0x00100000);
offsetLen = 4;
}else if (codec == "MP3" || codec == "MP2"){
// See RFC 2250, "MPEG Audio-specific header"
*((long *)(data + getHsize())) = 0; // this is MBZ and Frag_Offset, which are always 0
Bit::htobl(data + getHsize(), 0); // this is MBZ and Frag_Offset, which are always 0
if (payload[0] != 0xFF){FAIL_MSG("MP2/MP3 data does not start with header?");}
offsetLen = 4;
}else if (codec == "AC3"){
*((short *)(data + getHsize())) = htons(0x0001); // this is 6 bits MBZ, 2 bits FT = 0 = full
// frames and 8 bits saying we send 1 frame
Bit::htobs(data + getHsize(),
1); // this is 6 bits MBZ, 2 bits FT = 0 = full frames and 8 bits saying we send 1 frame
offsetLen = 2;
}
if (maxDataLen < getHsize() + offsetLen + payloadlen){
@ -289,8 +289,7 @@ namespace RTP{
increaseSequence();
}
void Packet::sendRTCP_SR(long long &connectedAt, void *socket, unsigned int tid, DTSC::Meta &metadata,
void callBack(void *, char *, unsigned int, unsigned int)){
void Packet::sendRTCP_SR(void *socket, void callBack(void *, const char *, size_t, uint8_t)){
char *rtcpData = (char *)malloc(32);
if (!rtcpData){
FAIL_MSG("Could not allocate 32 bytes. Something is seriously messed up.");
@ -311,8 +310,7 @@ namespace RTP{
free(rtcpData);
}
void Packet::sendRTCP_RR(long long &connectedAt, SDP::Track &sTrk, unsigned int tid, DTSC::Meta &metadata,
void callBack(void *, char *, unsigned int, unsigned int)){
void Packet::sendRTCP_RR(SDP::Track &sTrk, void callBack(void *, const char *, size_t, uint8_t)){
char *rtcpData = (char *)malloc(32);
if (!rtcpData){
FAIL_MSG("Could not allocate 32 bytes. Something is seriously messed up.");
@ -330,7 +328,7 @@ namespace RTP{
Bit::htobl(rtcpData + 20, 0); /// \TODO jitter (diff in timestamp vs packet arrival)
Bit::htobl(rtcpData + 24, 0); /// \TODO last SR (middle 32 bits of last SR or zero)
Bit::htobl(rtcpData + 28, 0); /// \TODO delay since last SR in 2b seconds + 2b fraction
callBack(&(sTrk.rtcp), (char *)rtcpData, 32, 0);
callBack(&(sTrk.rtcp), rtcpData, 32, 0);
sTrk.sorter.lostCurrent = 0;
sTrk.sorter.packCurrent = 0;
free(rtcpData);
@ -344,8 +342,7 @@ namespace RTP{
sentPackets = 0;
}
Packet::Packet(unsigned int payloadType, unsigned int sequence, unsigned int timestamp,
unsigned int ssrc, unsigned int csrcCount){
Packet::Packet(uint32_t payloadType, uint32_t sequence, uint64_t timestamp, uint32_t ssrc, uint32_t csrcCount){
managed = true;
data = new char[12 + 4 * csrcCount + 2 + MAX_SEND]; // headerSize, 2 for FU-A, MAX_SEND for maximum sent size
if (data){
@ -409,7 +406,7 @@ namespace RTP{
Packet::~Packet(){
if (managed){delete[] data;}
}
Packet::Packet(const char *dat, unsigned int len){
Packet::Packet(const char *dat, uint64_t len){
managed = false;
maxDataLen = len;
sentBytes = 0;
@ -496,7 +493,7 @@ namespace RTP{
while (packBuffer.count(rtpSeq)){
outPacket(packTrack, packBuffer[rtpSeq]);
packBuffer.erase(rtpSeq);
VERYHIGH_MSG("Sent packet %u, now %llu in buffer", rtpSeq, packBuffer.size());
INFO_MSG("Sent packet %u, now %zu in buffer", rtpSeq, packBuffer.size());
++rtpSeq;
++packTotal;
++packCurrent;
@ -506,7 +503,7 @@ namespace RTP{
while (packBuffer.count(rtpSeq)){
outPacket(packTrack, packBuffer[rtpSeq]);
packBuffer.erase(rtpSeq);
VERYHIGH_MSG("Sent packet %u, now %llu in buffer", rtpSeq, packBuffer.size());
INFO_MSG("Sent packet %u, now %zu in buffer", rtpSeq, packBuffer.size());
++rtpSeq;
++packTotal;
++packCurrent;
@ -542,7 +539,7 @@ namespace RTP{
cbPack = 0;
cbInit = 0;
multiplier = 1.0;
trackId = 0;
trackId = INVALID_TRACK_ID;
firstTime = 0;
packCount = 0;
lastSeq = 0;
@ -572,10 +569,12 @@ namespace RTP{
}
}
void toDTSC::setProperties(const DTSC::Track &Trk){
double m = (double)Trk.rate / 1000.0;
if (Trk.type == "video" || Trk.codec == "MP2" || Trk.codec == "MP3"){m = 90.0;}
setProperties(Trk.trackID, Trk.codec, Trk.type, Trk.init, m);
void toDTSC::setProperties(const DTSC::Meta &M, size_t tid){
double m = (double)M.getRate(tid) / 1000.0;
if (M.getType(tid) == "video" || M.getCodec(tid) == "MP2" || M.getCodec(tid) == "MP3"){
m = 90.0;
}
setProperties(tid, M.getCodec(tid), M.getType(tid), M.getInit(tid), m);
}
void toDTSC::setCallbacks(void (*cbP)(const DTSC::Packet &pkt),
@ -610,11 +609,12 @@ namespace RTP{
}
prevTime = pkt.getTimeStamp();
uint64_t msTime = ((uint64_t)pTime - firstTime + 1 + 0xFFFFFFFFull * wrapArounds) / multiplier;
char *pl = pkt.getPayload();
char *pl = (char *)pkt.getPayload();
uint32_t plSize = pkt.getPayloadSize();
bool missed = lastSeq != (pkt.getSequence() - 1);
lastSeq = pkt.getSequence();
INSANE_MSG("Received RTP packet for track %llu, time %llu -> %llu", trackId, pkt.getTimeStamp(), msTime);
INSANE_MSG("Received RTP packet for track %" PRIu64 ", time %" PRIu32 " -> %" PRIu64, trackId,
pkt.getTimeStamp(), msTime);
// From here on, there is codec-specific parsing. We call handler functions for each codec,
// except for the trivial codecs.
if (codec == "H264"){
@ -749,7 +749,7 @@ namespace RTP{
}
void toDTSC::handleHEVCSingle(uint64_t ts, const char *buffer, const uint32_t len, bool isKey){
MEDIUM_MSG("H265: %llu@%llu, %lub%s", trackId, ts, len, isKey ? " (key)" : "");
MEDIUM_MSG("H265: %" PRIu64 "@%" PRIu64 ", %" PRIu32 "b%s", trackId, ts, len, isKey ? " (key)" : "");
// Ignore zero-length packets (e.g. only contained init data and nothing else)
if (!len){return;}
@ -787,11 +787,13 @@ namespace RTP{
offset = (frameNo - packCount) * (1000.0 / fps);
//... and the timestamp is the packet counter times the frame rate in ms.
newTs = packCount * (1000.0 / fps);
VERYHIGH_MSG("Packing time %llu = %sframe %llu (%.2f FPS). Expected %llu -> +%llu/%lu", ts,
isKey ? "key" : "i", frameNo, fps, packCount, (frameNo - packCount), offset);
VERYHIGH_MSG("Packing time %" PRIu64 " = %sframe %" PRIu64 " (%.2f FPS). Expected %" PRIu64
" -> +%" PRIu64 "/%" PRIu32,
ts, (isKey ? "key" : "i"), frameNo, fps, packCount, (frameNo - packCount), offset);
}else{
// For non-steady frame rate, assume no offsets are used and the timestamp is already correct
VERYHIGH_MSG("Packing time %llu = %sframe %llu (variable rate)", ts, isKey ? "key" : "i", packCount);
VERYHIGH_MSG("Packing time %" PRIu64 " = %sframe %" PRIu64 " (variable rate)", ts,
isKey ? "key" : "i", packCount);
}
// Fill the new DTSC packet, buffer it.
DTSC::Packet nextPack;
@ -893,7 +895,7 @@ namespace RTP{
}
void toDTSC::handleH264Single(uint64_t ts, const char *buffer, const uint32_t len, bool isKey){
MEDIUM_MSG("H264: %llu@%llu, %lub%s", trackId, ts, len, isKey ? " (key)" : "");
MEDIUM_MSG("H264: %" PRIu64 "@%" PRIu64 ", %" PRIu32 "b%s", trackId, ts, len, isKey ? " (key)" : "");
// Ignore zero-length packets (e.g. only contained init data and nothing else)
if (!len){return;}
@ -976,12 +978,14 @@ namespace RTP{
offset = (frameNo - packCount) * (1000.0 / fps);
//... and the timestamp is the packet counter times the frame rate in ms.
newTs = packCount * (1000.0 / fps);
VERYHIGH_MSG("Packing time %llu = %sframe %llu (%.2f FPS). Expected %llu -> +%llu/%lu", ts,
isKey ? "key" : "i", frameNo, fps, packCount, (frameNo - packCount), offset);
VERYHIGH_MSG("Packing time %" PRIu64 " = %sframe %" PRIu64 " (%.2f FPS). Expected %" PRIu64
" -> +%" PRIu64 "/%" PRIu32,
ts, isKey ? "key" : "i", frameNo, fps, packCount, (frameNo - packCount), offset);
}else{
// For non-steady frame rate, assume no offsets are used and the timestamp is already
// correct
VERYHIGH_MSG("Packing time %llu = %sframe %llu (variable rate)", ts, isKey ? "key" : "i", packCount);
VERYHIGH_MSG("Packing time %" PRIu64 " = %sframe %" PRIu64 " (variable rate)", ts,
isKey ? "key" : "i", packCount);
}
// Fill the new DTSC packet, buffer it.
DTSC::Packet nextPack;
@ -1007,11 +1011,13 @@ namespace RTP{
offset = (frameNo - packCount) * (1000.0 / fps);
//... and the timestamp is the packet counter times the frame rate in ms.
newTs = packCount * (1000.0 / fps);
VERYHIGH_MSG("Packing time %llu = %sframe %llu (%.2f FPS). Expected %llu -> +%llu/%lu", ts,
isKey ? "key" : "i", frameNo, fps, packCount, (frameNo - packCount), offset);
VERYHIGH_MSG("Packing time %" PRIu64 " = %sframe %" PRIu64 " (%.2f FPS). Expected %" PRIu64
" -> +%" PRIu64 "/%" PRIu32,
ts, isKey ? "key" : "i", frameNo, fps, packCount, (frameNo - packCount), offset);
}else{
// For non-steady frame rate, assume no offsets are used and the timestamp is already correct
VERYHIGH_MSG("Packing time %llu = %sframe %llu (variable rate)", ts, isKey ? "key" : "i", packCount);
VERYHIGH_MSG("Packing time %" PRIu64 " = %sframe %" PRIu64 " (variable rate)", ts,
isKey ? "key" : "i", packCount);
}
// Fill the new DTSC packet, buffer it.
DTSC::Packet nextPack;

View file

@ -25,7 +25,7 @@ namespace SDP{
/// This namespace holds all RTP-parsing and sending related functionality.
namespace RTP{
extern unsigned int MAX_SEND;
extern uint32_t MAX_SEND;
/// This class is used to make RTP packets. Currently, H264, and AAC are supported. RTP
/// mechanisms, like increasing sequence numbers and setting timestamps are all taken care of in
@ -35,49 +35,47 @@ namespace RTP{
bool managed;
char *data; ///< The actual RTP packet that is being sent
uint32_t maxDataLen; ///< Amount of reserved bytes for the packet(s)
int sentPackets;
int sentBytes; // Because ugly is beautiful
uint32_t sentPackets;
uint32_t sentBytes; // Because ugly is beautiful
public:
static double startRTCP;
unsigned int getHsize() const;
unsigned int getPayloadSize() const;
uint32_t getHsize() const;
uint32_t getPayloadSize() const;
char *getPayload() const;
unsigned int getVersion() const;
unsigned int getPadding() const;
unsigned int getExtension() const;
unsigned int getContribCount() const;
unsigned int getMarker() const;
unsigned int getPayloadType() const;
unsigned int getSequence() const;
uint32_t getVersion() const;
uint32_t getPadding() const;
uint32_t getExtension() const;
uint32_t getContribCount() const;
uint32_t getMarker() const;
uint32_t getPayloadType() const;
uint16_t getSequence() const;
uint32_t getTimeStamp() const;
void setSequence(unsigned int seq);
unsigned int getSSRC() const;
void setSSRC(unsigned long ssrc);
void setSequence(uint16_t seq);
uint32_t getSSRC() const;
void setSSRC(uint32_t ssrc);
void setTimestamp(uint32_t t);
void increaseSequence();
void sendH264(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel, bool lastOfAccesUnit);
void sendVP8(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
void sendH264(void *socket, void callBack(void *, const char *, size_t, uint8_t), const char *payload,
unsigned int payloadlen, unsigned int channel, bool lastOfAccessUnit);
void sendVP8(void *socket, void callBack(void *, const char *, size_t, uint8_t),
const char *payload, unsigned int payloadlen, unsigned int channel);
void sendH265(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
void sendH265(void *socket, void callBack(void *, const char *, size_t, uint8_t),
const char *payload, unsigned int payloadlen, unsigned int channel);
void sendMPEG2(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
void sendMPEG2(void *socket, void callBack(void *, const char *, size_t, uint8_t),
const char *payload, unsigned int payloadlen, unsigned int channel);
void sendData(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel, std::string codec);
void sendRTCP_SR(long long &connectedAt, void *socket, unsigned int tid, DTSC::Meta &metadata,
void callBack(void *, char *, unsigned int, unsigned int));
void sendRTCP_RR(long long &connectedAt, SDP::Track &sTrk, unsigned int tid,
DTSC::Meta &metadata, void callBack(void *, char *, unsigned int, unsigned int));
void sendData(void *socket, void callBack(void *, const char *, size_t, uint8_t), const char *payload,
unsigned int payloadlen, unsigned int channel, std::string codec);
void sendRTCP_SR(void *socket, void callBack(void *, const char *, size_t, uint8_t));
void sendRTCP_RR(SDP::Track &sTrk, void callBack(void *, const char *, size_t, uint8_t));
Packet();
Packet(unsigned int pt, unsigned int seq, unsigned int ts, unsigned int ssr, unsigned int csrcCount = 0);
Packet(uint32_t pt, uint32_t seq, uint64_t ts, uint32_t ssr, uint32_t csrcCount = 0);
Packet(const Packet &o);
void operator=(const Packet &o);
~Packet();
Packet(const char *dat, unsigned int len);
char *getData();
Packet(const char *dat, uint64_t len);
const char *getData();
char *ptr() const{return data;}
};
@ -130,7 +128,7 @@ namespace RTP{
toDTSC();
void setProperties(const uint64_t track, const std::string &codec, const std::string &type,
const std::string &init, const double multiplier);
void setProperties(const DTSC::Track &Trk);
void setProperties(const DTSC::Meta &M, size_t tid);
void setCallbacks(void (*cbPack)(const DTSC::Packet &pkt),
void (*cbInit)(const uint64_t track, const std::string &initData));
void addRTP(const RTP::Packet &rPkt);

View file

@ -240,7 +240,7 @@ namespace RTP{
size_t maskNumBytes = getNumBytesUsedForMask();
if (maskNumBytes != 2 && maskNumBytes != 6){
FAIL_MSG("Invalid mask size (%u) cannot extract sequence numbers.", maskNumBytes);
FAIL_MSG("Invalid mask size (%zu) cannot extract sequence numbers.", maskNumBytes);
return false;
}
@ -259,7 +259,7 @@ namespace RTP{
for (uint16_t byteDX = 0; byteDX < maskNumBytes; ++byteDX){
uint8_t maskByte = maskPtr[byteDX];
for (uint16_t bitDX = 0; bitDX < 8; ++bitDX){
if (maskByte & (1 << 7 - bitDX)){
if (maskByte & ((1 << 7) - bitDX)){
uint16_t seqNum = seqNumBase + (byteDX << 3) + bitDX;
coveredSeqNums.insert(seqNum);
}
@ -502,7 +502,7 @@ namespace RTP{
Packet recreatedPacket;
fec->tryToRecoverMissingPacket(packetHistory, recreatedPacket);
if (recreatedPacket.ptr() != NULL){
char *pl = recreatedPacket.getPayload();
char *pl = (char *)recreatedPacket.getPayload();
WARN_MSG(" => reconstructed %u, %02X %02X %02X %02X | %02X %02X %02X %02X",
recreatedPacket.getSequence(), pl[0], pl[1], pl[2], pl[3], pl[4], pl[5], pl[6], pl[7]);
addPacket(recreatedPacket);
@ -531,7 +531,7 @@ namespace RTP{
}
void FECPacket::sendRTCP_RR(RTP::FECSorter &sorter, uint32_t mySSRC, uint32_t theirSSRC, void *userData,
void callBack(void *userData, const char *payload, uint32_t nbytes)){
void callBack(void *userData, const char *payload, size_t nbytes, uint8_t channel)){
char *rtcpData = (char *)malloc(32);
if (!rtcpData){
FAIL_MSG("Could not allocate 32 bytes. Something is seriously messed up.");
@ -545,11 +545,12 @@ namespace RTP{
Bit::htobl(rtcpData + 8, theirSSRC); // set source identifier
rtcpData[12] = (sorter.lostCurrent * 255) / (sorter.lostCurrent + sorter.packCurrent); // fraction lost since prev RR
Bit::htob24(rtcpData + 13, sorter.lostTotal); // cumulative packets lost since start
Bit::htobl(rtcpData + 16, sorter.rtpSeq | (sorter.packTotal & 0xFFFF0000ul)); // highest sequence received
Bit::htobl(rtcpData + 16,
sorter.rtpSeq | (sorter.packTotal & 0xFFFF0000ul)); // highest sequence received
Bit::htobl(rtcpData + 20, 0); /// \TODO jitter (diff in timestamp vs packet arrival)
Bit::htobl(rtcpData + 24, 0); /// \TODO last SR (middle 32 bits of last SR or zero)
Bit::htobl(rtcpData + 28, 0); /// \TODO delay since last SR in 2b seconds + 2b fraction
callBack(userData, rtcpData, 32);
callBack(userData, rtcpData, 32, 0);
sorter.lostCurrent = 0;
sorter.packCurrent = 0;
free(rtcpData);

View file

@ -86,7 +86,7 @@ namespace RTP{
class FECPacket : public Packet{
public:
void sendRTCP_RR(RTP::FECSorter &sorter, uint32_t mySSRC, uint32_t theirSSRC, void *userData,
void callBack(void *userData, const char *payload, uint32_t nbytes));
void callBack(void *userData, const char *payload, size_t nbytes, uint8_t channel));
};
}// namespace RTP

View file

@ -45,24 +45,26 @@ namespace SDP{
}
/// Gets the SDP contents for sending out a particular given DTSC::Track.
std::string mediaDescription(const DTSC::Track &trk){
std::string mediaDescription(const DTSC::Meta *meta, size_t tid){
const DTSC::Meta &M = *meta;
std::stringstream mediaDesc;
if (trk.codec == "H264"){
std::string codec = M.getCodec(tid);
std::string init = M.getInit(tid);
if (codec == "H264"){
MP4::AVCC avccbox;
avccbox.setPayload(trk.init);
avccbox.setPayload(init);
mediaDesc << "m=video 0 RTP/AVP 97\r\n"
"a=rtpmap:97 H264/90000\r\n"
"a=cliprect:0,0,"
<< trk.height << "," << trk.width
<< "\r\n"
"a=framesize:97 "
<< trk.width << '-' << trk.height
<< M.getHeight(tid) << "," << M.getWidth(tid) << "\r\na=framesize:97 "
<< M.getWidth(tid) << '-' << M.getHeight(tid)
<< "\r\n"
"a=fmtp:97 packetization-mode=1;profile-level-id="
<< std::hex << std::setw(2) << std::setfill('0') << (int)trk.init.data()[1]
<< std::dec << "E0" << std::hex << std::setw(2) << std::setfill('0')
<< (int)trk.init.data()[3] << std::dec << ";"
<< "sprop-parameter-sets=";
<< std::hex << std::setw(2) << std::setfill('0') << (int)init.data()[1] << std::dec
<< "E0" << std::hex << std::setw(2) << std::setfill('0') << (int)init.data()[3]
<< std::dec << ";sprop-parameter-sets=";
size_t count = avccbox.getSPSCount();
for (size_t i = 0; i < count; ++i){
mediaDesc << (i ? "," : "")
@ -75,19 +77,15 @@ namespace SDP{
<< Encodings::Base64::encode(std::string(avccbox.getPPS(i), avccbox.getPPSLen(i)));
}
mediaDesc << "\r\n"
<< "a=framerate:" << ((double)trk.fpks) / 1000.0
<< "\r\n"
"a=control:track"
<< trk.trackID << "\r\n";
}else if (trk.codec == "HEVC"){
h265::initData iData(trk.init);
"a=framerate:"
<< ((double)M.getFpks(tid)) / 1000.0 << "\r\na=control:track" << tid << "\r\n";
}else if (codec == "HEVC"){
h265::initData iData(init);
mediaDesc << "m=video 0 RTP/AVP 104\r\n"
"a=rtpmap:104 H265/90000\r\n"
"a=cliprect:0,0,"
<< trk.height << "," << trk.width
<< "\r\n"
"a=framesize:104 "
<< trk.width << '-' << trk.height << "\r\n"
<< M.getHeight(tid) << "," << M.getWidth(tid) << "\r\na=framesize:104 "
<< M.getWidth(tid) << '-' << M.getHeight(tid) << "\r\n"
<< "a=fmtp:104 sprop-vps=";
const std::set<std::string> &vps = iData.getVPS();
if (vps.size()){
@ -112,90 +110,80 @@ namespace SDP{
mediaDesc << Encodings::Base64::encode(*it);
}
}
mediaDesc << "\r\na=framerate:" << ((double)trk.fpks) / 1000.0
<< "\r\n"
"a=control:track"
<< trk.trackID << "\r\n";
}else if (trk.codec == "MPEG2"){
mediaDesc << "\r\na=framerate:" << ((double)M.getFpks(tid)) / 1000.0 << "\r\na=control:track"
<< tid << "\r\n";
}else if (codec == "MPEG2"){
mediaDesc << "m=video 0 RTP/AVP 32\r\n"
"a=cliprect:0,0,"
<< trk.height << "," << trk.width
<< "\r\n"
"a=framesize:32 "
<< trk.width << '-' << trk.height << "\r\n"
<< "a=framerate:" << ((double)trk.fpks) / 1000.0 << "\r\n"
<< "a=control:track" << trk.trackID << "\r\n";
}else if (trk.codec == "AAC"){
<< M.getHeight(tid) << "," << M.getWidth(tid) << "\r\na=framesize:32 " << M.getWidth(tid)
<< '-' << M.getHeight(tid) << "\r\na=framerate:" << ((double)M.getFpks(tid)) / 1000.0
<< "\r\na=control:track" << tid << "\r\n";
}else if (codec == "AAC"){
mediaDesc << "m=audio 0 RTP/AVP 96"
<< "\r\n"
"a=rtpmap:96 mpeg4-generic/"
<< trk.rate << "/" << trk.channels
<< M.getRate(tid) << "/" << M.getChannels(tid)
<< "\r\n"
"a=fmtp:96 streamtype=5; profile-level-id=15; config=";
for (unsigned int i = 0; i < trk.init.size(); i++){
mediaDesc << std::hex << std::setw(2) << std::setfill('0') << (int)trk.init[i] << std::dec;
for (unsigned int i = 0; i < init.size(); i++){
mediaDesc << std::hex << std::setw(2) << std::setfill('0') << (int)init[i] << std::dec;
}
// these values are described in RFC 3640
mediaDesc << "; mode=AAC-hbr; SizeLength=13; IndexLength=3; IndexDeltaLength=3;\r\n"
"a=control:track"
<< trk.trackID << "\r\n";
}else if (trk.codec == "MP3" || trk.codec == "MP2"){
mediaDesc << "m=" << trk.type << " 0 RTP/AVP 14"
<< tid << "\r\n";
}else if (codec == "MP3" || codec == "MP2"){
mediaDesc << "m=" << M.getType(tid) << " 0 RTP/AVP 14"
<< "\r\n"
"a=rtpmap:14 MPA/90000/"
<< trk.channels
<< "\r\n"
"a=control:track"
<< trk.trackID << "\r\n";
}else if (trk.codec == "AC3"){
<< M.getChannels(tid) << "\r\n"
<< "a=control:track" << tid << "\r\n";
}else if (codec == "AC3"){
mediaDesc << "m=audio 0 RTP/AVP 100"
<< "\r\n"
"a=rtpmap:100 AC3/"
<< trk.rate << "/" << trk.channels
<< "\r\n"
"a=control:track"
<< trk.trackID << "\r\n";
}else if (trk.codec == "ALAW"){
if (trk.channels == 1 && trk.rate == 8000){
<< M.getRate(tid) << "/" << M.getChannels(tid) << "\r\n"
<< "a=control:track" << tid << "\r\n";
}else if (codec == "ALAW"){
if (M.getChannels(tid) == 1 && M.getRate(tid) == 8000){
mediaDesc << "m=audio 0 RTP/AVP 8"
<< "\r\n";
}else{
mediaDesc << "m=audio 0 RTP/AVP 101"
<< "\r\n";
mediaDesc << "a=rtpmap:101 PCMA/" << trk.rate << "/" << trk.channels << "\r\n";
mediaDesc << "a=rtpmap:101 PCMA/" << M.getRate(tid) << "/" << M.getChannels(tid) << "\r\n";
}
mediaDesc << "a=control:track" << trk.trackID << "\r\n";
}else if (trk.codec == "ULAW"){
if (trk.channels == 1 && trk.rate == 8000){
mediaDesc << "a=control:track" << tid << "\r\n";
}else if (codec == "ULAW"){
if (M.getChannels(tid) == 1 && M.getRate(tid) == 8000){
mediaDesc << "m=audio 0 RTP/AVP 0"
<< "\r\n";
}else{
mediaDesc << "m=audio 0 RTP/AVP 104"
<< "\r\n";
mediaDesc << "a=rtpmap:104 PCMU/" << trk.rate << "/" << trk.channels << "\r\n";
mediaDesc << "a=rtpmap:104 PCMU/" << M.getRate(tid) << "/" << M.getChannels(tid) << "\r\n";
}
mediaDesc << "a=control:track" << trk.trackID << "\r\n";
}else if (trk.codec == "PCM"){
if (trk.size == 16 && trk.channels == 2 && trk.rate == 44100){
mediaDesc << "a=control:track" << tid << "\r\n";
}else if (codec == "PCM"){
if (M.getSize(tid) == 16 && M.getChannels(tid) == 2 && M.getRate(tid) == 44100){
mediaDesc << "m=audio 0 RTP/AVP 10"
<< "\r\n";
}else if (trk.size == 16 && trk.channels == 1 && trk.rate == 44100){
}else if (M.getSize(tid) == 16 && M.getChannels(tid) == 1 && M.getRate(tid) == 44100){
mediaDesc << "m=audio 0 RTP/AVP 11"
<< "\r\n";
}else{
mediaDesc << "m=audio 0 RTP/AVP 103"
<< "\r\n";
mediaDesc << "a=rtpmap:103 L" << trk.size << "/" << trk.rate << "/" << trk.channels << "\r\n";
mediaDesc << "a=rtpmap:103 L" << M.getSize(tid) << "/" << M.getRate(tid) << "/"
<< M.getChannels(tid) << "\r\n";
}
mediaDesc << "a=control:track" << trk.trackID << "\r\n";
}else if (trk.codec == "opus"){
mediaDesc << "a=control:track" << tid << "\r\n";
}else if (codec == "opus"){
mediaDesc << "m=audio 0 RTP/AVP 102"
<< "\r\n"
"a=rtpmap:102 opus/"
<< trk.rate << "/" << trk.channels
<< "\r\n"
"a=control:track"
<< trk.trackID << "\r\n";
<< M.getRate(tid) << "/" << M.getChannels(tid) << "\r\n"
<< "a=control:track" << tid << "\r\n";
}
return mediaDesc.str();
}
@ -232,43 +220,44 @@ namespace SDP{
/// \source The source identifier.
/// \return True if successful, false otherwise.
bool Track::parseTransport(const std::string &transport, const std::string &host,
const std::string &source, const DTSC::Track &trk){
if (trk.codec == "H264"){
const std::string &source, const DTSC::Meta *M, size_t tid){
std::string codec = M->getCodec(tid);
if (codec == "H264"){
pack = RTP::Packet(97, 1, 0, mySSRC);
}else if (trk.codec == "HEVC"){
}else if (codec == "HEVC"){
pack = RTP::Packet(104, 1, 0, mySSRC);
}else if (trk.codec == "MPEG2"){
}else if (codec == "MPEG2"){
pack = RTP::Packet(32, 1, 0, mySSRC);
}else if (trk.codec == "AAC"){
}else if (codec == "AAC"){
pack = RTP::Packet(96, 1, 0, mySSRC);
}else if (trk.codec == "AC3"){
}else if (codec == "AC3"){
pack = RTP::Packet(100, 1, 0, mySSRC);
}else if (trk.codec == "MP3" || trk.codec == "MP2"){
}else if (codec == "MP3" || codec == "MP2"){
pack = RTP::Packet(14, 1, 0, mySSRC);
}else if (trk.codec == "ALAW"){
if (trk.channels == 1 && trk.rate == 8000){
}else if (codec == "ALAW"){
if (M->getChannels(tid) == 1 && M->getRate(tid) == 8000){
pack = RTP::Packet(8, 1, 0, mySSRC);
}else{
pack = RTP::Packet(101, 1, 0, mySSRC);
}
}else if (trk.codec == "ULAW"){
if (trk.channels == 1 && trk.rate == 8000){
}else if (codec == "ULAW"){
if (M->getChannels(tid) == 1 && M->getRate(tid) == 8000){
pack = RTP::Packet(0, 1, 0, mySSRC);
}else{
pack = RTP::Packet(104, 1, 0, mySSRC);
}
}else if (trk.codec == "PCM"){
if (trk.size == 16 && trk.channels == 2 && trk.rate == 44100){
}else if (codec == "PCM"){
if (M->getSize(tid) == 16 && M->getChannels(tid) == 2 && M->getRate(tid) == 44100){
pack = RTP::Packet(10, 1, 0, mySSRC);
}else if (trk.size == 16 && trk.channels == 1 && trk.rate == 44100){
}else if (M->getSize(tid) == 16 && M->getChannels(tid) == 1 && M->getRate(tid) == 44100){
pack = RTP::Packet(11, 1, 0, mySSRC);
}else{
pack = RTP::Packet(103, 1, 0, mySSRC);
}
}else if (trk.codec == "opus"){
}else if (codec == "opus"){
pack = RTP::Packet(102, 1, 0, mySSRC);
}else{
ERROR_MSG("Unsupported codec %s for RTSP on track %u", trk.codec.c_str(), trk.trackID);
ERROR_MSG("Unsupported codec %s for RTSP on track %zu", codec.c_str(), tid);
return false;
}
if (transport.find("TCP") != std::string::npos){
@ -334,10 +323,10 @@ namespace SDP{
}
/// Gets the rtpInfo for a given DTSC::Track, source identifier and timestamp (in millis).
std::string Track::rtpInfo(const DTSC::Track &trk, const std::string &source, uint64_t currentTime){
std::string Track::rtpInfo(const DTSC::Meta &M, size_t tid, const std::string &source, uint64_t currentTime){
std::stringstream rInfo;
rInfo << "url=" << source << "/track" << trk.trackID << ";"; // get the current url, not localhost
rInfo << "sequence=" << pack.getSequence() << ";rtptime=" << currentTime * getMultiplier(trk);
rInfo << "url=" << source << "/track" << tid << ";"; // get the current url, not localhost
rInfo << "seq=" << pack.getSequence() << ";rtptime=" << currentTime * getMultiplier(&M, tid);
return rInfo.str();
}
@ -348,12 +337,11 @@ namespace SDP{
}
void State::parseSDP(const std::string &sdp){
DONTEVEN_MSG("Parsing %llu-byte SDP", sdp.size());
DONTEVEN_MSG("Parsing %zu-byte SDP", sdp.size());
std::stringstream ss(sdp);
std::string to;
uint64_t trackNo = 0;
size_t tid = INVALID_TRACK_ID;
bool nope = true; // true if we have no valid track to fill
DTSC::Track *thisTrack = 0;
while (std::getline(ss, to, '\n')){
if (!to.empty() && *to.rbegin() == '\r'){to.erase(to.size() - 1, 1);}
if (to.empty()){continue;}
@ -362,24 +350,23 @@ namespace SDP{
// All tracks start with a media line
if (to.substr(0, 2) == "m="){
nope = true;
++trackNo;
thisTrack = &(myMeta->tracks[trackNo]);
tid = myMeta->addTrack();
std::stringstream words(to.substr(2));
std::string item;
if (getline(words, item, ' ') && (item == "audio" || item == "video")){
thisTrack->type = item;
thisTrack->trackID = trackNo;
myMeta->setType(tid, item);
myMeta->setID(tid, tid);
}else{
WARN_MSG("Media type not supported: %s", item.c_str());
myMeta->tracks.erase(trackNo);
tracks.erase(trackNo);
myMeta->removeTrack(tid);
tracks.erase(tid);
continue;
}
getline(words, item, ' ');
if (!getline(words, item, ' ') || item.substr(0, 7) != "RTP/AVP"){
WARN_MSG("Media transport not supported: %s", item.c_str());
myMeta->tracks.erase(trackNo);
tracks.erase(trackNo);
myMeta->removeTrack(tid);
tracks.erase(tid);
continue;
}
if (getline(words, item, ' ')){
@ -388,62 +375,62 @@ namespace SDP{
case 0: // PCM Mu-law
INFO_MSG("PCM Mu-law payload type");
nope = false;
thisTrack->codec = "ULAW";
thisTrack->rate = 8000;
thisTrack->channels = 1;
myMeta->setCodec(tid, "ULAW");
myMeta->setRate(tid, 8000);
myMeta->setChannels(tid, 1);
break;
case 8: // PCM A-law
INFO_MSG("PCM A-law payload type");
nope = false;
thisTrack->codec = "ALAW";
thisTrack->rate = 8000;
thisTrack->channels = 1;
myMeta->setCodec(tid, "ALAW");
myMeta->setRate(tid, 8000);
myMeta->setChannels(tid, 1);
break;
case 10: // PCM Stereo, 44.1kHz
INFO_MSG("Linear PCM stereo 44.1kHz payload type");
nope = false;
thisTrack->codec = "PCM";
thisTrack->size = 16;
thisTrack->rate = 44100;
thisTrack->channels = 2;
myMeta->setCodec(tid, "PCM");
myMeta->setSize(tid, 16);
myMeta->setRate(tid, 44100);
myMeta->setChannels(tid, 2);
break;
case 11: // PCM Mono, 44.1kHz
INFO_MSG("Linear PCM mono 44.1kHz payload type");
nope = false;
thisTrack->codec = "PCM";
thisTrack->rate = 44100;
thisTrack->size = 16;
thisTrack->channels = 1;
myMeta->setCodec(tid, "PCM");
myMeta->setRate(tid, 44100);
myMeta->setSize(tid, 16);
myMeta->setChannels(tid, 1);
break;
case 14: // MPA
INFO_MSG("MPA payload type");
nope = false;
thisTrack->codec = "MP3";
thisTrack->rate = 0;
thisTrack->size = 0;
thisTrack->channels = 0;
myMeta->setCodec(tid, "MP3");
myMeta->setRate(tid, 0);
myMeta->setSize(tid, 0);
myMeta->setChannels(tid, 0);
break;
case 32: // MPV
INFO_MSG("MPV payload type");
nope = false;
thisTrack->codec = "MPEG2";
myMeta->setCodec(tid, "MPEG2");
break;
default:
// dynamic type
if (avp_type >= 96 && avp_type <= 127){
HIGH_MSG("Dynamic payload type (%llu) detected", avp_type);
HIGH_MSG("Dynamic payload type (%" PRIu64 ") detected", avp_type);
nope = false;
continue;
}else{
FAIL_MSG("Payload type %llu not supported!", avp_type);
myMeta->tracks.erase(trackNo);
tracks.erase(trackNo);
FAIL_MSG("Payload type %" PRIu64 " not supported!", avp_type);
myMeta->removeTrack(tid);
tracks.erase(tid);
continue;
}
}
}
tConv[trackNo].setProperties(*thisTrack);
HIGH_MSG("Incoming track %s", thisTrack->getIdentifier().c_str());
tConv[tid].setProperties(*myMeta, tid);
HIGH_MSG("Incoming track %s", myMeta->getTrackIdentifier(tid).c_str());
continue;
}
@ -456,62 +443,62 @@ namespace SDP{
for (unsigned int i = 0; i < trCodec.size(); ++i){
if (trCodec[i] <= 122 && trCodec[i] >= 97){trCodec[i] -= 32;}
}
if (thisTrack->type == "audio"){
if (myMeta->getType(tid) == "audio"){
std::string extraInfo = mediaType.substr(mediaType.find('/') + 1);
if (extraInfo.find('/') != std::string::npos){
size_t lastSlash = extraInfo.find('/');
thisTrack->rate = atoll(extraInfo.substr(0, lastSlash).c_str());
thisTrack->channels = atoll(extraInfo.substr(lastSlash + 1).c_str());
myMeta->setRate(tid, atoll(extraInfo.substr(0, lastSlash).c_str()));
myMeta->setChannels(tid, atoll(extraInfo.substr(lastSlash + 1).c_str()));
}else{
thisTrack->rate = atoll(extraInfo.c_str());
thisTrack->channels = 1;
myMeta->setRate(tid, atoll(extraInfo.c_str()));
myMeta->setChannels(tid, 1);
}
}
if (trCodec == "H264"){
thisTrack->codec = "H264";
thisTrack->rate = 90000;
myMeta->setCodec(tid, "H264");
myMeta->setRate(tid, 90000);
}
if (trCodec == "H265"){
thisTrack->codec = "HEVC";
thisTrack->rate = 90000;
myMeta->setCodec(tid, "HEVC");
myMeta->setRate(tid, 90000);
}
if (trCodec == "OPUS"){
thisTrack->codec = "opus";
thisTrack->init = std::string("OpusHead\001\002\170\000\200\273\000\000\000\000\000", 19);
myMeta->setCodec(tid, "opus");
myMeta->setInit(tid, "OpusHead\001\002\170\000\200\273\000\000\000\000\000", 19);
}
if (trCodec == "PCMA"){thisTrack->codec = "ALAW";}
if (trCodec == "PCMU"){thisTrack->codec = "ULAW";}
if (trCodec == "PCMA"){myMeta->setCodec(tid, "ALAW");}
if (trCodec == "PCMU"){myMeta->setCodec(tid, "ULAW");}
if (trCodec == "L8"){
thisTrack->codec = "PCM";
thisTrack->size = 8;
myMeta->setCodec(tid, "PCM");
myMeta->setSize(tid, 8);
}
if (trCodec == "L16"){
thisTrack->codec = "PCM";
thisTrack->size = 16;
myMeta->setCodec(tid, "PCM");
myMeta->setSize(tid, 16);
}
if (trCodec == "L20"){
thisTrack->codec = "PCM";
thisTrack->size = 20;
myMeta->setCodec(tid, "PCM");
myMeta->setSize(tid, 20);
}
if (trCodec == "L24" || trCodec == "PCM"){
thisTrack->codec = "PCM";
thisTrack->size = 24;
myMeta->setCodec(tid, "PCM");
myMeta->setSize(tid, 24);
}
if (trCodec == "MPEG4-GENERIC"){thisTrack->codec = "AAC";}
if (!thisTrack->codec.size()){
if (trCodec == "MPEG4-GENERIC"){myMeta->setCodec(tid, "AAC");}
if (!myMeta->getCodec(tid).size()){
ERROR_MSG("Unsupported RTP mapping: %s", mediaType.c_str());
}else{
tConv[trackNo].setProperties(*thisTrack);
HIGH_MSG("Incoming track %s", thisTrack->getIdentifier().c_str());
tConv[tid].setProperties(*myMeta, tid);
HIGH_MSG("Incoming track %s", myMeta->getTrackIdentifier(tid).c_str());
}
continue;
}
if (to.substr(0, 10) == "a=control:"){
tracks[trackNo].control = to.substr(10);
tracks[tid].control = to.substr(10);
continue;
}
if (to.substr(0, 12) == "a=framerate:"){
if (!thisTrack->rate){thisTrack->rate = atof(to.c_str() + 12) * 1000;}
if (!myMeta->getRate(tid)){myMeta->setRate(tid, atof(to.c_str() + 12) * 1000);}
continue;
}
if (to.substr(0, 12) == "a=framesize:"){
@ -525,75 +512,76 @@ namespace SDP{
continue;
}
if (to.substr(0, 7) == "a=fmtp:"){
tracks[trackNo].fmtp = to.substr(7);
if (thisTrack->codec == "AAC"){
if (tracks[trackNo].getParamString("mode") != "AAC-hbr"){
tracks[tid].fmtp = to.substr(7);
if (myMeta->getCodec(tid) == "AAC"){
if (tracks[tid].getParamString("mode") != "AAC-hbr"){
// a=fmtp:97
// profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;
// config=120856E500
FAIL_MSG("AAC transport mode not supported: %s", tracks[trackNo].getParamString("mode").c_str());
FAIL_MSG("AAC transport mode not supported: %s", tracks[tid].getParamString("mode").c_str());
nope = true;
myMeta->tracks.erase(trackNo);
tracks.erase(trackNo);
myMeta->removeTrack(tid);
tracks.erase(tid);
continue;
}
thisTrack->init = Encodings::Hex::decode(tracks[trackNo].getParamString("config"));
myMeta->setInit(tid, Encodings::Hex::decode(tracks[tid].getParamString("config")));
// myMeta.tracks[trackNo].rate = aac::AudSpecConf::rate(myMeta.tracks[trackNo].init);
}
if (thisTrack->codec == "H264"){
if (myMeta->getCodec(tid) == "H264"){
// a=fmtp:96 packetization-mode=1;
// sprop-parameter-sets=Z0LAHtkA2D3m//AUABqxAAADAAEAAAMAMg8WLkg=,aMuDyyA=;
// profile-level-id=42C01E
std::string sprop = tracks[trackNo].getParamString("sprop-parameter-sets");
std::string sprop = tracks[tid].getParamString("sprop-parameter-sets");
size_t comma = sprop.find(',');
tracks[trackNo].spsData = Encodings::Base64::decode(sprop.substr(0, comma));
tracks[trackNo].ppsData = Encodings::Base64::decode(sprop.substr(comma + 1));
updateH264Init(trackNo);
tracks[tid].spsData = Encodings::Base64::decode(sprop.substr(0, comma));
tracks[tid].ppsData = Encodings::Base64::decode(sprop.substr(comma + 1));
updateH264Init(tid);
}
if (thisTrack->codec == "HEVC"){
tracks[trackNo].hevcInfo.addUnit(Encodings::Base64::decode(tracks[trackNo].getParamString("sprop-vps")));
tracks[trackNo].hevcInfo.addUnit(Encodings::Base64::decode(tracks[trackNo].getParamString("sprop-sps")));
tracks[trackNo].hevcInfo.addUnit(Encodings::Base64::decode(tracks[trackNo].getParamString("sprop-pps")));
updateH265Init(trackNo);
if (myMeta->getCodec(tid) == "HEVC"){
tracks[tid].hevcInfo.addUnit(
Encodings::Base64::decode(tracks[tid].getParamString("sprop-vps")));
tracks[tid].hevcInfo.addUnit(
Encodings::Base64::decode(tracks[tid].getParamString("sprop-sps")));
tracks[tid].hevcInfo.addUnit(
Encodings::Base64::decode(tracks[tid].getParamString("sprop-pps")));
updateH265Init(tid);
}
continue;
}
// We ignore bandwidth lines
if (to.substr(0, 2) == "b="){continue;}
// we ignore everything before the first media line.
if (!trackNo){continue;}
if (tid == INVALID_TRACK_ID){continue;}
// at this point, the data is definitely for a track
INFO_MSG("Unhandled SDP line for track %llu: %s", trackNo, to.c_str());
INFO_MSG("Unhandled SDP line for track %zu: %s", tid, to.c_str());
}
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta->tracks.begin();
it != myMeta->tracks.end(); ++it){
INFO_MSG("Detected track %s", it->second.getIdentifier().c_str());
std::set<size_t> validTracks = myMeta->getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
INFO_MSG("Detected track %s", myMeta->getTrackIdentifier(*it).c_str());
}
}
/// Calculates H265 track metadata from sps and pps data stored in tracks[trackNo]
void State::updateH265Init(uint64_t trackNo){
DTSC::Track &Trk = myMeta->tracks[trackNo];
SDP::Track &RTrk = tracks[trackNo];
void State::updateH265Init(size_t tid){
SDP::Track &RTrk = tracks[tid];
if (!RTrk.hevcInfo.haveRequired()){
MEDIUM_MSG("Aborted meta fill for hevc track %lu: no info nal unit", trackNo);
MEDIUM_MSG("Aborted meta fill for hevc track %lu: no info nal unit", tid);
return;
}
Trk.init = RTrk.hevcInfo.generateHVCC();
myMeta->setInit(tid, RTrk.hevcInfo.generateHVCC());
h265::metaInfo MI = tracks[trackNo].hevcInfo.getMeta();
h265::metaInfo MI = tracks[tid].hevcInfo.getMeta();
RTrk.fpsMeta = MI.fps;
Trk.width = MI.width;
Trk.height = MI.height;
Trk.fpks = RTrk.fpsMeta * 1000;
tConv[trackNo].setProperties(Trk);
myMeta->setWidth(tid, MI.width);
myMeta->setHeight(tid, MI.height);
myMeta->setFpks(tid, RTrk.fpsMeta * 1000);
tConv[tid].setProperties(*myMeta, tid);
}
/// Calculates H264 track metadata from vps, sps and pps data stored in tracks[trackNo]
void State::updateH264Init(uint64_t trackNo){
DTSC::Track &Trk = myMeta->tracks[trackNo];
SDP::Track &RTrk = tracks[trackNo];
void State::updateH264Init(uint64_t tid){
SDP::Track &RTrk = tracks[tid];
h264::sequenceParameterSet sps(RTrk.spsData.data(), RTrk.spsData.size());
h264::SPSMeta hMeta = sps.getCharacteristics();
MP4::AVCC avccBox;
@ -606,27 +594,27 @@ namespace SDP{
avccBox.setPPSCount(1);
avccBox.setPPS(RTrk.ppsData);
RTrk.fpsMeta = hMeta.fps;
Trk.width = hMeta.width;
Trk.height = hMeta.height;
Trk.fpks = hMeta.fps * 1000;
Trk.init = std::string(avccBox.payload(), avccBox.payloadSize());
tConv[trackNo].setProperties(Trk);
myMeta->setWidth(tid, hMeta.width);
myMeta->setHeight(tid, hMeta.height);
myMeta->setFpks(tid, hMeta.fps * 1000);
myMeta->setInit(tid, avccBox.payload(), avccBox.payloadSize());
tConv[tid].setProperties(*myMeta, tid);
}
uint32_t State::getTrackNoForChannel(uint8_t chan){
for (std::map<uint32_t, Track>::iterator it = tracks.begin(); it != tracks.end(); ++it){
size_t State::getTrackNoForChannel(uint8_t chan){
for (std::map<size_t, Track>::iterator it = tracks.begin(); it != tracks.end(); ++it){
if (chan == it->second.channel){return it->first;}
}
return 0;
return INVALID_TRACK_ID;
}
uint32_t State::parseSetup(HTTP::Parser &H, const std::string &cH, const std::string &src){
size_t State::parseSetup(HTTP::Parser &H, const std::string &cH, const std::string &src){
static uint32_t trackCounter = 0;
if (H.url == "200"){
++trackCounter;
if (!tracks.count(trackCounter)){return 0;}
if (!tracks[trackCounter].parseTransport(H.GetHeader("Transport"), cH, src, myMeta->tracks[trackCounter])){
return 0;
if (!tracks.count(trackCounter)){return INVALID_TRACK_ID;}
if (!tracks[trackCounter].parseTransport(H.GetHeader("Transport"), cH, src, myMeta, trackCounter)){
return INVALID_TRACK_ID;
}
return trackCounter;
}
@ -638,7 +626,7 @@ namespace SDP{
while (loop){
if (tracks.size()){
for (std::map<uint32_t, Track>::iterator it = tracks.begin(); it != tracks.end(); ++it){
for (std::map<size_t, Track>::iterator it = tracks.begin(); it != tracks.end(); ++it){
if (!it->second.control.size()){
it->second.control = "/track" + JSON::Value(it->first).asString();
INFO_MSG("Control track: %s", it->second.control.c_str());
@ -649,8 +637,8 @@ namespace SDP{
(pw.size() >= it->second.control.size() &&
pw.substr(pw.size() - it->second.control.size()) == it->second.control)){
INFO_MSG("Parsing SETUP against track %lu", it->first);
if (!it->second.parseTransport(H.GetHeader("Transport"), cH, src, myMeta->tracks[it->first])){
return 0;
if (!it->second.parseTransport(H.GetHeader("Transport"), cH, src, myMeta, it->first)){
return INVALID_TRACK_ID;
}
return it->first;
}
@ -658,13 +646,13 @@ namespace SDP{
}
if (H.url.find("/track") != std::string::npos){
uint32_t trackNo = atoi(H.url.c_str() + H.url.find("/track") + 6);
if (trackNo){
INFO_MSG("Parsing SETUP against track %lu", trackNo);
if (!tracks[trackNo].parseTransport(H.GetHeader("Transport"), cH, src, myMeta->tracks[trackNo])){
return 0;
}
return trackNo;
// if (trackNo){
INFO_MSG("Parsing SETUP against track %" PRIu32, trackNo);
if (!tracks[trackNo].parseTransport(H.GetHeader("Transport"), cH, src, myMeta, trackNo)){
return INVALID_TRACK_ID;
}
return trackNo;
//}
}
if (urlString != url.path){
urlString = url.path;
@ -672,18 +660,20 @@ namespace SDP{
loop = false;
}
}
return 0;
return INVALID_TRACK_ID;
}
/// Returns the multiplier to use to get milliseconds from the RTP payload type for the given
/// track
double getMultiplier(const DTSC::Track &Trk){
if (Trk.type == "video" || Trk.codec == "MP2" || Trk.codec == "MP3"){return 90.0;}
return ((double)Trk.rate / 1000.0);
double getMultiplier(const DTSC::Meta *M, size_t tid){
if (M->getType(tid) == "video" || M->getCodec(tid) == "MP2" || M->getCodec(tid) == "MP3"){
return 90.0;
}
return ((double)M->getRate(tid) / 1000.0);
}
void State::updateInit(const uint64_t trackNo, const std::string &initData){
if (myMeta->tracks.count(trackNo)){myMeta->tracks[trackNo].init = initData;}
void State::updateInit(const size_t tid, const std::string &initData){
myMeta->setInit(tid, initData.data(), initData.size());
}
/// Handles RTP packets generically, for both TCP and UDP-based connections.

View file

@ -7,7 +7,7 @@
namespace SDP{
double getMultiplier(const DTSC::Track &Trk);
double getMultiplier(const DTSC::Meta *M, size_t tid);
/// Structure used to keep track of selected tracks.
class Track{
@ -17,8 +17,8 @@ namespace SDP{
std::string getParamString(const std::string &param) const;
uint64_t getParamInt(const std::string &param) const;
bool parseTransport(const std::string &transport, const std::string &host,
const std::string &source, const DTSC::Track &trk);
std::string rtpInfo(const DTSC::Track &trk, const std::string &source, uint64_t currentTime);
const std::string &source, const DTSC::Meta *M, size_t tid);
std::string rtpInfo(const DTSC::Meta &M, size_t tid, const std::string &source, uint64_t currentTime);
public:
Socket::UDPConnection data;
@ -47,18 +47,18 @@ namespace SDP{
void (*incomingPacketCallback)(const DTSC::Packet &pkt);
void parseSDP(const std::string &sdp);
void parseSDPEx(const std::string &sdp);
void updateH264Init(uint64_t trackNo);
void updateH265Init(uint64_t trackNo);
void updateH264Init(size_t trackNo);
void updateH265Init(size_t tid);
void updateInit(const uint64_t trackNo, const std::string &initData);
uint32_t getTrackNoForChannel(uint8_t chan);
uint32_t parseSetup(HTTP::Parser &H, const std::string &host, const std::string &source);
size_t getTrackNoForChannel(uint8_t chan);
size_t parseSetup(HTTP::Parser &H, const std::string &host, const std::string &source);
void handleIncomingRTP(const uint64_t track, const RTP::Packet &pkt);
public:
DTSC::Meta *myMeta;
std::map<uint32_t, RTP::toDTSC> tConv; ///< Converters to DTSC
std::map<uint32_t, Track> tracks; ///< List of selected tracks with SDP-specific session data.
std::map<size_t, RTP::toDTSC> tConv; ///< Converters to DTSC
std::map<size_t, Track> tracks; ///< List of selected tracks with SDP-specific session data.
};
std::string mediaDescription(const DTSC::Track &trk);
std::string mediaDescription(const DTSC::Meta *M, size_t tid);
}// namespace SDP

View file

@ -488,7 +488,7 @@ namespace SDP{
MediaFormat *Media::getFormatForPayloadType(uint64_t &payloadType){
std::map<uint64_t, MediaFormat>::iterator it = formats.find(payloadType);
if (it == formats.end()){
ERROR_MSG("No format found for payload type: %u.", payloadType);
ERROR_MSG("No format found for payload type: %" PRIu64 ".", payloadType);
return NULL;
}
return &it->second;
@ -581,6 +581,14 @@ namespace SDP{
return false;
}
bool Session::hasSendOnlyMedia(){
size_t numMedias = medias.size();
for (size_t i = 0; i < numMedias; ++i){
if (medias[i].direction == "sendonly"){return true;}
}
return false;
}
bool Session::parseSDP(const std::string &sdp){
if (sdp.empty()){
@ -760,7 +768,7 @@ namespace SDP{
}
Answer::Answer()
: isVideoEnabled(false), isAudioEnabled(false), candidatePort(0),
: isAudioEnabled(false), isVideoEnabled(false), candidatePort(0),
videoLossPrevention(SDP_LOSS_PREVENTION_NONE){}
bool Answer::parseOffer(const std::string &sdp){
@ -820,45 +828,44 @@ namespace SDP{
direction = dir;
}
bool Answer::setupVideoDTSCTrack(DTSC::Track &result){
bool Answer::setupVideoDTSCTrack(DTSC::Meta &M, size_t tid){
if (!isVideoEnabled){
FAIL_MSG("Video is disabled; cannot setup DTSC::Track.");
return false;
}
result.codec = codecRTP2Mist(answerVideoFormat.encodingName);
if (result.codec.empty()){
M.setCodec(tid, codecRTP2Mist(answerVideoFormat.encodingName));
if (M.getCodec(tid).empty()){
FAIL_MSG("Failed to convert the format codec into one that MistServer understands. %s.",
answerVideoFormat.encodingName.c_str());
return false;
}
result.type = "video";
result.rate = answerVideoFormat.getVideoRate();
result.trackID = answerVideoFormat.payloadType;
M.setType(tid, "video");
M.setRate(tid, answerVideoFormat.getVideoRate());
M.setID(tid, answerVideoFormat.payloadType);
INFO_MSG("Setup video track %zu for payload type %zu", tid, answerVideoFormat.payloadType);
return true;
}
bool Answer::setupAudioDTSCTrack(DTSC::Track &result){
bool Answer::setupAudioDTSCTrack(DTSC::Meta &M, size_t tid){
if (!isAudioEnabled){
FAIL_MSG("Audio is disabled; cannot setup DTSC::Track.");
return false;
}
result.codec = codecRTP2Mist(answerAudioFormat.encodingName);
if (result.codec.empty()){
M.setCodec(tid, codecRTP2Mist(answerAudioFormat.encodingName));
if (M.getCodec(tid).empty()){
FAIL_MSG("Failed to convert the format codec into one that MistServer understands. %s.",
answerAudioFormat.encodingName.c_str());
return false;
}
result.type = "audio";
result.rate = answerAudioFormat.getAudioSampleRate();
result.channels = answerAudioFormat.getAudioNumChannels();
result.size = answerAudioFormat.getAudioBitSize();
result.trackID = answerAudioFormat.payloadType;
M.setType(tid, "audio");
M.setRate(tid, answerAudioFormat.getAudioSampleRate());
M.setChannels(tid, answerAudioFormat.getAudioNumChannels());
M.setSize(tid, answerAudioFormat.getAudioBitSize());
M.setID(tid, answerAudioFormat.payloadType);
INFO_MSG("Setup audio track %zu for payload time %zu", tid, answerAudioFormat.payloadType);
return true;
}
@ -1023,7 +1030,9 @@ namespace SDP{
return result;
}
void Answer::addLine(const std::string &fmt, ...){
// The parameter here is NOT a reference, because va_start specifies that its parameter is not
// allowed to be one.
void Answer::addLine(const std::string fmt, ...){
char buffer[1024] ={};
va_list args;

View file

@ -50,7 +50,8 @@ namespace SDP{
uint64_t getPayloadType() const; ///< Returns the `payloadType` member.
int32_t getPacketizationModeForH264(); ///< When this represents a h264 format this will return the
///< packetization mode when it was provided in the SDP
std::string getProfileLevelIdForH264(); ///< When this represents a H264 format, this will return the profile-level-id from the format parameters.
std::string getProfileLevelIdForH264(); ///< When this represents a H264 format, this will return the
///< profile-level-id from the format parameters.
operator bool() const;
@ -144,6 +145,9 @@ namespace SDP{
bool hasReceiveOnlyMedia(); ///< Returns true when one of the media sections has a `a=recvonly`
///< attribute. This is used to determine if the other peer only
///< wants to receive or also sent data. */
bool hasSendOnlyMedia(); ///< Returns true when one of the media sections has a `a=sendonly`
///< attribute. This is used to determine if the other peer only
///< wants to receive or also sent data. */
public:
std::vector<SDP::Media> medias; ///< For each `m=` line we create a `SDP::Media` instance. The
@ -166,14 +170,14 @@ namespace SDP{
void setFingerprint(const std::string &fingerprintSha); ///< Set the SHA265 that represents the
///< certificate that is used with DTLS.
void setDirection(const std::string &dir);
bool setupVideoDTSCTrack(DTSC::Track &result);
bool setupAudioDTSCTrack(DTSC::Track &result);
bool setupVideoDTSCTrack(DTSC::Meta &M, size_t tid);
bool setupAudioDTSCTrack(DTSC::Meta &M, size_t tid);
std::string toString();
private:
bool enableMedia(const std::string &type, const std::string &codecName, SDP::Media &outMedia,
SDP::MediaFormat &outFormat);
void addLine(const std::string &fmt, ...);
void addLine(const std::string fmt, ...);
std::string generateSessionId();
std::string generateIceUFrag(); ///< Generates the `ice-ufrag` value.
std::string generateIcePwd(); ///< Generates the `ice-pwd` value.

View file

@ -22,11 +22,6 @@
#include <windows.h>
#endif
/// Forces a disconnect to all users.
static void killStatistics(char *data, size_t len, unsigned int id){
(*(data - 1)) = 60 | ((*(data - 1)) & 0x80); // Send disconnect message;
}
namespace IPC{
#if defined(__CYGWIN__) || defined(_WIN32)
@ -48,8 +43,9 @@ namespace IPC{
///\brief Constructs a named semaphore
///\param name The name of the semaphore
///\param oflag The flags with which to open the semaphore
///\param mode The mode in which to create the semaphore, if O_CREAT is given in oflag, ignored otherwise
///\param value The initial value of the semaphore if O_CREAT is given in oflag, ignored otherwise
///\param mode The mode in which to create the semaphore, if O_CREAT is given in oflag, ignored
/// otherwise \param value The initial value of the semaphore if O_CREAT is given in oflag,
/// ignored otherwise
semaphore::semaphore(const char *name, int oflag, mode_t mode, unsigned int value, bool noWait){
#if defined(__CYGWIN__) || defined(_WIN32)
mySem = 0;
@ -77,8 +73,9 @@ namespace IPC{
/// Closes currently opened semaphore if needed
///\param name The name of the semaphore
///\param oflag The flags with which to open the semaphore
///\param mode The mode in which to create the semaphore, if O_CREAT is given in oflag, ignored otherwise
///\param value The initial value of the semaphore if O_CREAT is given in oflag, ignored otherwise
///\param mode The mode in which to create the semaphore, if O_CREAT is given in oflag, ignored
/// otherwise \param value The initial value of the semaphore if O_CREAT is given in oflag,
/// ignored otherwise
void semaphore::open(const char *name, int oflag, mode_t mode, unsigned int value, bool noWait){
close();
int timer = 0;
@ -139,7 +136,8 @@ namespace IPC{
int semaphore::getVal() const{
#if defined(__CYGWIN__) || defined(_WIN32)
LONG res;
ReleaseSemaphore(mySem, 0, &res); // not really release.... just checking to see if I can get the value this way
ReleaseSemaphore(mySem, 0,
&res); // not really release.... just checking to see if I can get the value this way
#else
int res;
sem_getvalue(mySem, &res);
@ -202,7 +200,41 @@ namespace IPC{
return isLocked;
}
///\brief Tries to wait for the semaphore for a single second, returns true if successful, false otherwise
///\brief Tries to wait for the semaphore for a given amount of ms, returns true if successful, false
/// otherwise
bool semaphore::tryWait(uint64_t ms){
if (!(*this)){return false;}
int result;
#if defined(__CYGWIN__) || defined(_WIN32)
result = WaitForSingleObject(mySem, ms); // wait at most 1s
if (result == 0x80){
WARN_MSG("Consistency error caught on semaphore %s", myName.c_str());
result = 0;
}
#elif defined(__APPLE__)
/// \todo (roxlu) test tryWaitOneSecond, shared_memory.cpp
uint64_t now = Util::getMicros();
uint64_t timeout = now + (ms * 1000);
while (now < timeout){
if (0 == sem_trywait(mySem)){
isLocked = true;
return true;
}
usleep(100e3);
now = Util::getMicros();
}
return false;
#else
struct timespec wt;
wt.tv_sec = ms / 1000;
wt.tv_nsec = ms % 1000;
result = sem_timedwait(mySem, &wt);
#endif
return isLocked = (result == 0);
}
///\brief Tries to wait for the semaphore for a single second, returns true if successful, false
/// otherwise
bool semaphore::tryWaitOneSecond(){
if (!(*this)){return false;}
int result;
@ -598,581 +630,9 @@ namespace IPC{
///\brief Default destructor
sharedFile::~sharedFile(){close();}
///\brief StatExchange constructor, sets the datapointer to the given value
statExchange::statExchange(char *_data) : data(_data){}
///\brief Sets timestamp of the current stats
void statExchange::now(long long int time){Bit::htobll(data, time);}
///\brief Gets timestamp of the current stats
long long int statExchange::now(){
long long int result;
return Bit::btohll(data);
}
///\brief Sets time currently connected
void statExchange::time(long time){Bit::htobl(data + 8, time);}
/// Calculates session ID from CRC, stream name, connector and host.
std::string statExchange::getSessId(){return Secure::md5(data + 32, 140);}
///\brief Gets time currently connected
long statExchange::time(){return Bit::btohl(data + 8);}
///\brief Sets the last viewing second of this user
void statExchange::lastSecond(long time){Bit::htobl(data + 12, time);}
///\brief Gets the last viewing second of this user
long statExchange::lastSecond(){return Bit::btohl(data + 12);}
///\brief Sets the amount of bytes received
void statExchange::down(long long int bytes){Bit::htobll(data + 16, bytes);}
///\brief Gets the amount of bytes received
long long int statExchange::down(){return Bit::btohll(data + 16);}
///\brief Sets the amount of bytes sent
void statExchange::up(long long int bytes){Bit::htobll(data + 24, bytes);}
///\brief Gets the amount of bytes sent
long long int statExchange::up(){return Bit::btohll(data + 24);}
///\brief Sets the host of this connection
void statExchange::host(std::string name){
if (name.size() < 16){memset(data + 32, 0, 16);}
memcpy(data + 32, name.c_str(), std::min((int)name.size(), 16));
}
///\brief Gets the host of this connection
std::string statExchange::host(){return std::string(data + 32, 16);}
///\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){name[splitChar] = '+';}
snprintf(data + 48, 100, "%s", name.c_str());
}
///\brief Gets the name of the stream this user is viewing
std::string statExchange::streamName(){return std::string(data + 48, strnlen(data + 48, 100));}
///\brief Sets the name of the connector through which this user is viewing
void statExchange::connector(std::string name){snprintf(data + 148, 20, "%s", name.c_str());}
///\brief Gets the name of the connector through which this user is viewing
std::string statExchange::connector(){
return std::string(data + 148, std::min((int)strlen(data + 148), 20));
}
///\brief Sets checksum field
void statExchange::crc(unsigned int sum){Bit::htobl(data + 168, sum);}
///\brief Gets checksum field
unsigned int statExchange::crc(){return Bit::btohl(data + 168);}
///\brief Sets checksum field
void statExchange::setSync(char s){data[172] = s;}
///\brief Gets checksum field
char statExchange::getSync(){return data[172];}
///\brief Gets PID field
uint32_t statExchange::getPID(){return *(uint32_t *)(data + 173);}
///\brief Creates a semaphore guard, locks the semaphore on call
semGuard::semGuard(semaphore *thisSemaphore) : mySemaphore(thisSemaphore){mySemaphore->wait();}
///\brief Destructs a semaphore guard, unlocks the semaphore on call
semGuard::~semGuard(){mySemaphore->post();}
///\brief Default constructor, erases all the values
sharedServer::sharedServer(){
payLen = 0;
hasCounter = false;
amount = 0;
}
///\brief Desired constructor, initializes after cleaning.
///\param name The basename of this server
///\param len The lenght of the payload
///\param withCounter Whether the content should have a counter
sharedServer::sharedServer(std::string name, int len, bool withCounter){
sharedServer();
init(name, len, withCounter);
}
///\brief Initialize the server
///\param name The basename of this server
///\param len The lenght of the payload
///\param withCounter Whether the content should have a counter
void sharedServer::init(std::string name, int len, bool withCounter){
if (mySemaphore){mySemaphore.close();}
if (baseName != ""){mySemaphore.unlink();}
myPages.clear();
baseName = "/" + name;
payLen = len;
hasCounter = withCounter;
mySemaphore.open(baseName.c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1);
if (!mySemaphore){
DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return;
}else{
semGuard tmpGuard(&mySemaphore);
amount = 0;
newPage();
}
}
///\brief The deconstructor
sharedServer::~sharedServer(){mySemaphore.unlink();}
///\brief Determines whether a sharedServer is valid
sharedServer::operator bool() const{return myPages.size();}
/// Sets all currently loaded memory pages to non-master, so they are not cleaned up on
/// destruction, but left behind. Useful for doing rolling updates and such.
void sharedServer::abandon(){
if (!myPages.size()){return;}
VERYHIGH_MSG("Abandoning %llu memory pages, leaving them behind on purpose", myPages.size());
for (std::deque<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++){
(*it).master = false;
}
}
///\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)), false, false);
if (!tmp.mapped){
tmp.init(std::string(baseName.substr(1) + (char)(myPages.size() + (int)'A')),
std::min(((8192 * 2) << myPages.size()), (32 * 1024 * 1024)), true);
tmp.master = false;
}
myPages.push_back(tmp);
myPages.back().master = true;
VERYHIGH_MSG("Created a new page: %s", tmp.name.c_str());
amount += (32 * 1024 * 1024) * myPages.size(); // assume maximum load - we don't want to miss any entries
}
///\brief Deletes the highest allocated page
void sharedServer::deletePage(){
if (myPages.size() == 1){
DEBUG_MSG(DLVL_WARN, "Can't remove last page for %s", baseName.c_str());
return;
}
myPages.pop_back();
}
///\brief Determines whether an id is currently in use or not
bool sharedServer::isInUse(unsigned int id){
unsigned int i = 0;
for (std::deque<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++){
// return if we reached the end
if (!it->mapped || !it->len){return false;}
// not on this page? skip to next.
if (it->len < (id - i) * payLen){
i += it->len / payLen;
continue;
}
if (hasCounter){
// counter? return true if it is non-zero.
return (it->mapped[(id - i) * payLen] != 0);
}else{
// no counter - check the entire size for being all zeroes.
for (unsigned int j = 0; j < payLen; ++j){
if (it->mapped[(id - i) * payLen + j]){return true;}
}
return false;
}
}
// only happens if we run out of pages
return false;
}
/// Disconnect all connected users, waits at most 2.5 seconds until completed
void sharedServer::finishEach(){
if (!hasCounter){return;}
unsigned int c = 0; // to prevent eternal loops
do{
parseEach(killStatistics);
Util::wait(250);
}while (amount > 1 && c++ < 10);
}
/// Returns a pointer to the data for the given index.
/// Returns null on error or if index is empty.
char *sharedServer::getIndex(unsigned int requestId){
char *empty = 0;
if (!hasCounter){
empty = (char *)malloc(payLen * sizeof(char));
memset(empty, 0, payLen);
}
unsigned int id = 0;
for (std::deque<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++){
if (!it->mapped || !it->len){
DEBUG_MSG(DLVL_FAIL, "Something went terribly wrong?");
return 0;
}
unsigned int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= it->len){
if (id == requestId){
if (hasCounter){
if (it->mapped[offset] != 0){
return it->mapped + offset + 1;
}else{
return 0;
}
}else{
if (memcmp(empty, it->mapped + offset, payLen)){
return it->mapped + offset;
}else{
return 0;
}
}
}
offset += payLen + (hasCounter ? 1 : 0);
id++;
}
}
return 0;
}
///\brief Parse each of the possible payload pieces, and runs a callback on it if in use.
void sharedServer::parseEach(void (*activeCallback)(char *data, size_t len, unsigned int id),
void (*disconCallback)(char *data, size_t len, unsigned int id)){
char *empty = 0;
if (!hasCounter){
empty = (char *)malloc(payLen * sizeof(char));
memset(empty, 0, payLen);
}
unsigned int id = 0;
unsigned int userCount = 0;
unsigned int emptyCount = 0;
unsigned int lastFilled = 0;
connectedUsers = 0;
for (std::deque<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++){
if (!it->mapped || !it->len){
DEBUG_MSG(DLVL_FAIL, "Something went terribly wrong?");
break;
}
userCount = 0;
unsigned int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= it->len){
if (hasCounter){
if (it->mapped[offset] != 0){
char *counter = it->mapped + offset;
// increase the count if needed
++userCount;
if (*counter & 0x80){connectedUsers++;}
char countNum = (*counter) & 0x7F;
lastFilled = id;
if (id >= amount){
amount = id + 1;
VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
}
uint32_t tmpPID = *((uint32_t *)(it->mapped + 1 + offset + payLen - 4));
if (tmpPID > 1 && it->master && !Util::Procs::isRunning(tmpPID) &&
!(countNum == 126 || countNum == 127)){
WARN_MSG("process disappeared, timing out. (pid %lu)", tmpPID);
*counter = 125 | (0x80 & (*counter)); // if process is already dead, instant timeout.
}
activeCallback(it->mapped + offset + 1, payLen, id);
switch (countNum){
case 127: HIGH_MSG("Client %u requested disconnect", id); break;
case 126: HIGH_MSG("Client %u timed out", id); break;
default:
#ifndef NOCRASHCHECK
if (tmpPID > 1 && it->master){
if (countNum > 10 && countNum < 60){
if (countNum < 30){
if (countNum > 15){WARN_MSG("Process %d is unresponsive", tmpPID);}
Util::Procs::Stop(tmpPID); // soft kill
}else{
ERROR_MSG("Killing unresponsive process %d", tmpPID);
Util::Procs::Murder(tmpPID); // improved kill
}
}
if (countNum > 70){
if (countNum < 90){
if (countNum > 75){WARN_MSG("Stopping process %d is unresponsive", tmpPID);}
Util::Procs::Stop(tmpPID); // soft kill
}else{
ERROR_MSG("Killing unresponsive stopping process %d", tmpPID);
Util::Procs::Murder(tmpPID); // improved kill
}
}
}
#endif
break;
}
if (countNum == 127 || countNum == 126){
semGuard tmpGuard(&mySemaphore);
if (disconCallback){disconCallback(counter + 1, payLen, id);}
memset(counter + 1, 0, payLen);
*counter = 0;
}else{
++(*counter);
}
}else{
// stop if we're past the amount counted and we're empty
if (id >= amount){
// bring the counter down if this was the last element
if (lastFilled + 1 < amount){
amount = lastFilled + 1;
VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
}
if (id >= amount + 100){
// stop, we're guaranteed no more pages are full at this point
break;
}
}
}
}else{
if (memcmp(empty, it->mapped + offset, payLen)){
++userCount;
// increase the count if needed
lastFilled = id;
if (id >= amount){
amount = id + 1;
VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
}
activeCallback(it->mapped + offset, payLen, id);
}else{
// stop if we're past the amount counted and we're empty
if (id >= amount){
// bring the counter down if this was the last element
if (lastFilled + 1 < amount){
amount = lastFilled + 1;
VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
}
if (id >= amount + 100){
// stop, we're guaranteed no more pages are full at this point
if (empty){free(empty);}
break;
}
}
}
}
offset += payLen + (hasCounter ? 1 : 0);
id++;
}
if (userCount == 0){
++emptyCount;
}else{
emptyCount = 0;
std::deque<sharedPage>::iterator tIt = it;
if (++tIt == myPages.end()){
bool unsetMaster = !(it->master);
semGuard tmpGuard(&mySemaphore);
newPage();
if (unsetMaster){(myPages.end() - 1)->master = false;}
it = myPages.end() - 2;
}
}
}
if (emptyCount > 1){
semGuard tmpGuard(&mySemaphore);
deletePage();
}
if (empty){free(empty);}
}
///\brief Creates an empty shared client
sharedClient::sharedClient(){
hasCounter = 0;
payLen = 0;
offsetOnPage = 0;
countAsViewer = true;
}
///\brief Copy constructor for sharedClients
///\param rhs The client ro copy
sharedClient::sharedClient(const sharedClient &rhs){
countAsViewer = rhs.countAsViewer;
baseName = rhs.baseName;
payLen = rhs.payLen;
hasCounter = rhs.hasCounter;
#ifdef __APPLE__
// note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
#else
mySemaphore.open(baseName.c_str(), O_RDWR);
#endif
if (!mySemaphore){
DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return;
}
myPage.init(rhs.myPage.name, rhs.myPage.len, rhs.myPage.master);
offsetOnPage = rhs.offsetOnPage;
}
///\brief Assignment operator
void sharedClient::operator=(const sharedClient &rhs){
countAsViewer = rhs.countAsViewer;
baseName = rhs.baseName;
payLen = rhs.payLen;
hasCounter = rhs.hasCounter;
#ifdef __APPLE__
// note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
#else
mySemaphore.open(baseName.c_str(), O_RDWR);
#endif
if (!mySemaphore){
DEBUG_MSG(DLVL_FAIL, "Creating copy of semaphore %s failed: %s", baseName.c_str(), strerror(errno));
return;
}
myPage.init(rhs.myPage.name, rhs.myPage.len, rhs.myPage.master);
offsetOnPage = rhs.offsetOnPage;
}
///\brief SharedClient Constructor, allocates space on the correct page.
///\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){
countAsViewer = true;
#ifdef __APPLE__
// note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
#else
mySemaphore.open(baseName.c_str(), O_RDWR);
#endif
if (!mySemaphore){
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));
if (!empty){
DEBUG_MSG(DLVL_FAIL, "Failed to allocate %u bytes for empty payload!", payLen);
return;
}
memset(empty, 0, payLen);
}
uint32_t attempts = 0;
while (offsetOnPage == -1 && (++attempts) < 20){
for (char i = 'A'; i <= 'Z'; i++){
myPage.init(baseName.substr(1) + i, (4096 << (i - 'A')), false, false);
if (!myPage.mapped){break;}
int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= myPage.len){
if ((hasCounter && myPage.mapped[offset] == 0) ||
(!hasCounter && !memcmp(myPage.mapped + offset, empty, payLen))){
semGuard tmpGuard(&mySemaphore);
if ((hasCounter && myPage.mapped[offset] == 0) ||
(!hasCounter && !memcmp(myPage.mapped + offset, empty, payLen))){
offsetOnPage = offset;
if (hasCounter){
myPage.mapped[offset] = 1;
*((uint32_t *)(myPage.mapped + 1 + offset + len - 4)) = getpid();
HIGH_MSG("sharedClient received ID %d", offsetOnPage / (payLen + 1));
}
break;
}
}
offset += payLen + (hasCounter ? 1 : 0);
}
if (offsetOnPage != -1){break;}
}
if (offsetOnPage == -1){Util::wait(500);}
}
if (offsetOnPage == -1){
FAIL_MSG("Could not register on page for %s", baseName.c_str());
myPage.close();
}
if (empty){free(empty);}
}
///\brief The deconstructor
sharedClient::~sharedClient(){mySemaphore.close();}
///\brief Writes data to the shared data
void sharedClient::write(char *data, int len){
if (hasCounter){keepAlive();}
memcpy(myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0), data, std::min(len, payLen));
}
///\brief Indicate that the process is done using this piece of memory, set the counter to finished
void sharedClient::finish(){
if (!myPage.mapped){return;}
if (!hasCounter){
DEBUG_MSG(DLVL_WARN, "Trying to time-out an element without counters");
myPage.close();
return;
}
semGuard tmpGuard(&mySemaphore);
myPage.mapped[offsetOnPage] = 126 | (countAsViewer ? 0x80 : 0);
HIGH_MSG("sharedClient finished ID %d", offsetOnPage / (payLen + 1));
myPage.close();
}
///\brief Re-initialize the counter
void sharedClient::keepAlive(){
if (!hasCounter){
DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element without counters");
return;
}
if (isAlive()){myPage.mapped[offsetOnPage] = (countAsViewer ? 0x81 : 0x01);}
}
bool sharedClient::isAlive(){
if (!hasCounter){return (myPage.mapped != 0);}
if (myPage.mapped && offsetOnPage >= 0){return (myPage.mapped[offsetOnPage] & 0x7F) < 60;}
return false;
}
///\brief Get a pointer to the data of this client
char *sharedClient::getData(){
if (!myPage.mapped){return 0;}
return (myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0));
}
int sharedClient::getCounter(){
if (!hasCounter){return -1;}
if (!myPage.mapped){return 0;}
return *(myPage.mapped + offsetOnPage);
}
userConnection::userConnection(char *_data){
data = _data;
if (!data){WARN_MSG("userConnection created with null pointer!");}
}
unsigned long userConnection::getTrackId(size_t offset) const{
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;
}
return Bit::btohl(data + (offset * 6));
}
void userConnection::setTrackId(size_t offset, unsigned long trackId) const{
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){
WARN_MSG("Trying to get keynum for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
return 0;
}
return Bit::btohs(data + (offset * 6) + 4);
}
void userConnection::setKeynum(size_t offset, unsigned long keynum){
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);
}
}// namespace IPC

View file

@ -20,49 +20,6 @@
namespace IPC{
///\brief A class used for the exchange of statistics over shared memory.
class statExchange{
public:
statExchange(char *_data);
void now(long long int time);
long long int now();
void time(long time);
long time();
void lastSecond(long time);
long lastSecond();
void down(long long int bytes);
long long int down();
void up(long long int bytes);
long long int up();
void host(std::string name);
std::string host();
void streamName(std::string name);
std::string streamName();
void connector(std::string name);
std::string connector();
void crc(unsigned int sum);
char getSync();
void setSync(char s);
unsigned int crc();
uint32_t getPID();
std::string getSessId();
private:
///\brief The payload for the stat exchange
/// - 8 byte - now (timestamp of last statistics)
/// - 4 byte - time (duration of the current connection)
/// - 4 byte - lastSecond (last second of content viewed)
/// - 8 byte - down (Number of bytes received from peer)
/// - 8 byte - up (Number of bytes sent to peer)
/// - 16 byte - host (ip address of the peer)
/// - 100 byte - streamName (name of the stream peer is viewing)
/// - 20 byte - connector (name of the connector the peer is using)
/// - 4 byte - CRC32 of user agent (or zero if none)
/// - 1 byte sync (was seen by controller yes/no)
/// - (implicit 4 bytes: PID)
char *data;
};
///\brief A class used for the abstraction of semaphores
class semaphore{
public:
@ -75,6 +32,7 @@ namespace IPC{
void post();
void wait();
bool tryWait();
bool tryWait(uint64_t ms);
bool tryWaitOneSecond();
void close();
void abandon();
@ -158,7 +116,7 @@ namespace IPC{
///\brief The name of the opened shared memory page
std::string name;
///\brief The size in bytes of the opened shared memory page
long long int len;
uint64_t len;
///\brief Whether this class should unlink the shared memory upon deletion or not
bool master;
///\brief A pointer to the payload of the page
@ -174,96 +132,4 @@ namespace IPC{
~sharedPage();
};
#endif
///\brief The server part of a server/client model for shared memory.
///
/// The server manages the shared memory pages, and allocates new pages when needed.
///
/// Pages are created with a basename + index, where index is in the range of 'A' - 'Z'
/// Each time a page is nearly full, the next page is created with a size double to the previous one.
///
/// Clients should allocate payLen bytes at a time, possibly with the addition of a counter.
/// If no such length can be allocated, the next page should be tried, and so on.
class sharedServer{
public:
sharedServer();
sharedServer(std::string name, int len, bool withCounter = false);
void init(std::string name, int len, bool withCounter = false);
~sharedServer();
void parseEach(void (*activeCallback)(char *data, size_t len, unsigned int id),
void (*disconCallback)(char *data, size_t len, unsigned int id) = 0);
char *getIndex(unsigned int id);
operator bool() const;
///\brief The amount of connected clients
unsigned int amount;
unsigned int connectedUsers;
void finishEach();
void abandon();
private:
bool isInUse(unsigned int id);
void newPage();
void deletePage();
///\brief The basename of the shared pages.
std::string baseName;
///\brief The length of each consecutive piece of payload
unsigned int payLen;
///\brief The set of sharedPage structures to manage the actual memory
std::deque<sharedPage> myPages;
///\brief A semaphore that is locked upon creation and deletion of the page, to ensure no new data is allocated during this step.
semaphore mySemaphore;
///\brief Whether the payload has a counter, if so, it is added in front of the payload
bool hasCounter;
};
///\brief The client part of a server/client model for shared memory.
///
/// The server manages the shared memory pages, and allocates new pages when needed.
///
/// Pages are created with a basename + index, where index is in the range of 'A' - 'Z'
/// Each time a page is nearly full, the next page is created with a size double to the previous one.
///
/// Clients should allocate payLen bytes at a time, possibly with the addition of a counter.
/// If no such length can be allocated, the next page should be tried, and so on.
class sharedClient{
public:
sharedClient();
sharedClient(const sharedClient &rhs);
sharedClient(std::string name, int len, bool withCounter = false);
void operator=(const sharedClient &rhs);
~sharedClient();
void write(char *data, int len);
void finish();
void keepAlive();
bool isAlive();
char *getData();
int getCounter();
bool countAsViewer;
private:
///\brief The basename of the shared pages.
std::string baseName;
///\brief The shared page this client has reserved a space on.
sharedPage myPage;
///\brief A semaphore that is locked upon trying to allocate space on a page
semaphore mySemaphore;
///\brief The size in bytes of the opened page
int payLen;
///\brief The offset of the payload reserved for this client within the opened page
int offsetOnPage;
///\brief Whether the payload has a counter, if so, it is added in front of the payload
bool hasCounter;
};
class userConnection{
public:
userConnection(char *_data);
unsigned long getTrackId(size_t offset) const;
void setTrackId(size_t offset, unsigned long trackId) const;
unsigned long getKeynum(size_t offset) const;
void setKeynum(size_t offset, unsigned long keynum);
private:
char *data;
};
}// namespace IPC

View file

@ -15,8 +15,10 @@ public:
SRTPReader();
int init(const std::string &cipher, const std::string &key, const std::string &salt);
int shutdown();
int unprotectRtp(uint8_t *data, int *nbytes); /* `nbytes` should contain the number of bytes in `data`. On success `nbytes` will hold the number of bytes of the decoded RTP packet. */
int unprotectRtcp(uint8_t *data, int *nbytes); /* `nbytes` should contains the number of bytes in `data`. On success `nbytes` will hold the number of bytes the decoded RTCP packet. */
int unprotectRtp(uint8_t *data, int *nbytes); /* `nbytes` should contain the number of bytes in `data`. On success `nbytes`
will hold the number of bytes of the decoded RTP packet. */
int unprotectRtcp(uint8_t *data, int *nbytes); /* `nbytes` should contains the number of bytes in `data`. On success `nbytes`
will hold the number of bytes the decoded RTCP packet. */
private:
srtp_t session;

View file

@ -253,20 +253,6 @@ JSON::Value Util::getGlobalConfig(const std::string &optionName){
}
}
DTSC::Meta Util::getStreamMeta(const std::string &streamname){
DTSC::Meta ret;
char pageId[NAME_BUFFER_SIZE];
snprintf(pageId, NAME_BUFFER_SIZE, SHM_STREAM_INDEX, streamname.c_str());
IPC::sharedPage mPage(pageId, DEFAULT_STRM_PAGE_SIZE);
if (!mPage.mapped){
FAIL_MSG("Could not connect to metadata for %s", streamname.c_str());
return ret;
}
DTSC::Packet tmpMeta(mPage.mapped, mPage.len, true);
if (tmpMeta.getVersion()){ret.reinit(tmpMeta);}
return ret;
}
/// Checks if the given streamname has an active input serving it. Returns true if this is the case.
/// Assumes the streamname has already been through sanitizeName()!
bool Util::streamAlive(std::string &streamname){
@ -696,12 +682,16 @@ DTSC::Scan Util::DTSCShmReader::getScan(){
return DTSC::Scan(rAcc.getPointer("dtsc_data"), rAcc.getSize("dtsc_data"));
}
std::set<size_t> Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, const std::string &trackType,
const std::string &trackVal, const std::string &UA){
/*LTS-START*/
/// Selects a specific track or set of tracks of the given trackType, using trackVal to decide.
/// trackVal may be a comma-separated list of numbers, codecs or the word "all" or an asterisk.
/// Does not do any checks if the protocol supports these tracks, just selects blindly.
/// It is necessary to follow up with a selectDefaultTracks() call to strip unsupported
/// codecs/combinations.
std::set<size_t> Util::findTracks(const DTSC::Meta &M, const std::string &trackType, const std::string &trackVal){
std::set<size_t> result;
if (!trackVal.size() || trackVal == "0" || trackVal == "-1" || trackVal == "none"){
return result;
}// don't select anything in particular
if (!trackVal.size()){return result;}
if (trackVal == "-1" | trackVal == "none"){return result;}// don't select anything in particular
if (trackVal.find(',') != std::string::npos){
// Comma-separated list, recurse.
std::stringstream ss(trackVal);
@ -712,36 +702,27 @@ std::set<size_t> Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa,
}
return result;
}
{
size_t trackNo = JSON::Value(trackVal).asInt();
if (trackVal == JSON::Value((uint64_t)trackNo).asString()){
// It's an integer number
if (!M.tracks.count(trackNo)){
INFO_MSG("Track %zd does not exist in stream, cannot select", trackNo);
return result;
}
const DTSC::Track &Trk = M.tracks.at(trackNo);
if (Trk.type != trackType && Trk.codec != trackType){
INFO_MSG("Track %zd is not %s (%s/%s), cannot select", trackNo, trackType.c_str(),
Trk.type.c_str(), Trk.codec.c_str());
return result;
}
INFO_MSG("Selecting %s track %zd (%s/%s)", trackType.c_str(), trackNo, Trk.type.c_str(),
Trk.codec.c_str());
result.insert(trackNo);
size_t idx = JSON::Value(trackVal).asInt();
if (trackVal == JSON::Value(idx).asString()){
if (!M.trackValid(idx)){
WARN_MSG("Track %zu does not exist in stream, cannot select", idx);
return result;
}
if (M.getType(idx) != trackType && M.getCodec(idx) != trackType){
WARN_MSG("Track %zu is not %s (%s/%s), cannot select", idx, trackType.c_str(),
M.getType(idx).c_str(), M.getCodec(idx).c_str());
return result;
}
result.insert(idx);
return result;
}
std::string trackLow = trackVal;
Util::stringToLower(trackLow);
if (trackLow == "all" || trackLow == "*"){
// select all tracks of this type
std::set<size_t> validTracks = getSupportedTracks(M, capa);
std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
const DTSC::Track &Trk = M.tracks.at(*it);
if (!trackType.size() || Trk.type == trackType || Trk.codec == trackType){
result.insert(*it);
}
if (M.getType(*it) == trackType || M.getCodec(*it) == trackType){result.insert(*it);}
}
return result;
}
@ -960,27 +941,12 @@ std::set<size_t> Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa,
// attempt to do language/codec matching
// convert 2-character language codes into 3-character language codes
if (trackLow.size() == 2){trackLow = Encodings::ISO639::twoToThree(trackLow);}
std::set<size_t> validTracks = getSupportedTracks(M, capa);
std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
const DTSC::Track &Trk = M.tracks.at(*it);
if (!trackType.size() || Trk.type == trackType || Trk.codec == trackType){
std::string codecLow = Trk.codec;
if (M.getType(*it) == trackType || M.getCodec(*it) == trackType){
std::string codecLow = M.getCodec(*it);
Util::stringToLower(codecLow);
if (Trk.lang == trackLow || trackLow == codecLow){result.insert(*it);}
if (!trackType.size() || trackType == "video"){
unsigned int resX, resY;
if (trackLow == "720p" && Trk.width == 1280 && Trk.height == 720){result.insert(*it);}
if (trackLow == "1080p" && Trk.width == 1920 && Trk.height == 1080){result.insert(*it);}
if (trackLow == "1440p" && Trk.width == 2560 && Trk.height == 1440){result.insert(*it);}
if (trackLow == "2k" && Trk.width == 2048 && Trk.height == 1080){result.insert(*it);}
if (trackLow == "4k" && Trk.width == 3840 && Trk.height == 2160){result.insert(*it);}
if (trackLow == "5k" && Trk.width == 5120 && Trk.height == 2880){result.insert(*it);}
if (trackLow == "8k" && Trk.width == 7680 && Trk.height == 4320){result.insert(*it);}
// match "XxY" format
if (sscanf(trackLow.c_str(), "%ux%u", &resX, &resY) == 2){
if (Trk.width == resX && Trk.height == resY){result.insert(*it);}
}
}
if (M.getLang(*it) == trackLow || trackLow == codecLow){result.insert(*it);}
}
}
return result;
@ -996,14 +962,19 @@ std::set<size_t> Util::wouldSelect(const DTSC::Meta &M, const std::string &track
std::set<size_t> Util::getSupportedTracks(const DTSC::Meta &M, const JSON::Value &capa,
const std::string &type, const std::string &UA){
std::set<size_t> validTracks;
for (std::map<unsigned int, DTSC::Track>::const_iterator it = M.tracks.begin(); it != M.tracks.end(); it++){
const DTSC::Track &Trk = it->second;
if (type != "" && type != Trk.type){continue;}
std::set<size_t> validTracks = M.getValidTracks();
std::set<size_t> toRemove;
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
// Remove unrequested tracks
if (type != "" && type != M.getType(*it)){
toRemove.insert(*it);
continue;
}
// Remove tracks for which we don't have codec support
if (capa.isMember("codecs")){
std::string codec = Trk.codec;
std::string type = Trk.type;
std::string codec = M.getCodec(*it);
std::string type = M.getType(*it);
bool found = false;
jsonForEachConst(capa["codecs"], itb){
jsonForEachConst(*itb, itc){
@ -1040,11 +1011,30 @@ std::set<size_t> Util::getSupportedTracks(const DTSC::Meta &M, const JSON::Value
if (found){break;}
}
if (!found){
HIGH_MSG("Track %u with codec %s not supported!", it->first, codec.c_str());
HIGH_MSG("Track %zu with codec %s not supported!", *it, codec.c_str());
toRemove.insert(*it);
continue;
}
}
validTracks.insert(it->first);
// Remove encrypted tracks if not supported
if (M.getEncryption(*it) != ""){
std::string encryptionType = M.getEncryption(*it);
encryptionType = encryptionType.substr(0, encryptionType.find('/'));
bool found = false;
jsonForEach(capa["encryption"], itb){
if (itb->asStringRef() == encryptionType){
found = true;
break;
}
}
if (!found){
INFO_MSG("Track %zu with encryption type %s not supported!", *it, encryptionType.c_str());
toRemove.insert(*it);
}
}
}
for (std::set<size_t>::iterator it = toRemove.begin(); it != toRemove.end(); it++){
validTracks.erase(*it);
}
return validTracks;
}
@ -1153,9 +1143,8 @@ std::set<size_t> Util::wouldSelect(const DTSC::Meta &M, const std::map<std::stri
++shift;
}
for (std::set<size_t>::iterator itd = result.begin(); itd != result.end(); itd++){
const DTSC::Track &Trk = M.tracks.at(*itd);
if ((!byType && Trk.codec == strRef.substr(shift)) ||
(byType && Trk.type == strRef.substr(shift)) || strRef.substr(shift) == "*"){
if ((!byType && M.getCodec(*itd) == strRef.substr(shift)) ||
(byType && M.getType(*itd) == strRef.substr(shift)) || strRef.substr(shift) == "*"){
// user-agent-check
bool problems = false;
if (capa.isMember("exceptions") && capa["exceptions"].isObject() &&
@ -1208,9 +1197,8 @@ std::set<size_t> Util::wouldSelect(const DTSC::Meta &M, const std::map<std::stri
++shift;
}
for (std::set<size_t>::iterator itd = result.begin(); itd != result.end(); itd++){
const DTSC::Track &Trk = M.tracks.at(*itd);
if ((!byType && Trk.codec == strRef.substr(shift)) ||
(byType && Trk.type == strRef.substr(shift)) || strRef.substr(shift) == "*"){
if ((!byType && M.getCodec(*itd) == strRef.substr(shift)) ||
(byType && M.getType(*itd) == strRef.substr(shift)) || strRef.substr(shift) == "*"){
// user-agent-check
bool problems = false;
if (capa.isMember("exceptions") && capa["exceptions"].isObject() && capa["exceptions"].size()){
@ -1242,12 +1230,11 @@ std::set<size_t> Util::wouldSelect(const DTSC::Meta &M, const std::map<std::stri
++shift;
}
if (found && !multiSel){continue;}
if (M.live){
if (M.getLive()){
for (std::set<size_t>::reverse_iterator trit = validTracks.rbegin();
trit != validTracks.rend(); trit++){
const DTSC::Track &Trk = M.tracks.at(*trit);
if ((!byType && Trk.codec == strRef.substr(shift)) ||
(byType && Trk.type == strRef.substr(shift)) || strRef.substr(shift) == "*"){
if ((!byType && M.getCodec(*trit) == strRef.substr(shift)) ||
(byType && M.getType(*trit) == strRef.substr(shift)) || strRef.substr(shift) == "*"){
// user-agent-check
bool problems = false;
if (capa.isMember("exceptions") && capa["exceptions"].isObject() &&
@ -1259,12 +1246,15 @@ std::set<size_t> Util::wouldSelect(const DTSC::Meta &M, const std::map<std::stri
}
}
}
// if (!allowBFrames && M.hasBFrames(*trit)){problems = true;}
if (!allowBFrames && M.hasBFrames(*trit)){problems = true;}
if (problems){break;}
/*LTS-START*/
if (noSelAudio && Trk.type == "audio"){continue;}
if (noSelVideo && Trk.type == "video"){continue;}
if (noSelSub && (Trk.type == "subtitle" || Trk.codec == "subtitle")){continue;}
if (noSelAudio && M.getType(*trit) == "audio"){continue;}
if (noSelVideo && M.getType(*trit) == "video"){continue;}
if (noSelSub &&
(M.getType(*trit) == "subtitle" || M.getCodec(*trit) == "subtitle")){
continue;
}
/*LTS-END*/
result.insert(*trit);
found = true;
@ -1273,9 +1263,8 @@ std::set<size_t> Util::wouldSelect(const DTSC::Meta &M, const std::map<std::stri
}
}else{
for (std::set<size_t>::iterator trit = validTracks.begin(); trit != validTracks.end(); trit++){
const DTSC::Track &Trk = M.tracks.at(*trit);
if ((!byType && Trk.codec == strRef.substr(shift)) ||
(byType && Trk.type == strRef.substr(shift)) || strRef.substr(shift) == "*"){
if ((!byType && M.getCodec(*trit) == strRef.substr(shift)) ||
(byType && M.getType(*trit) == strRef.substr(shift)) || strRef.substr(shift) == "*"){
// user-agent-check
bool problems = false;
if (capa.isMember("exceptions") && capa["exceptions"].isObject() &&
@ -1287,12 +1276,15 @@ std::set<size_t> Util::wouldSelect(const DTSC::Meta &M, const std::map<std::stri
}
}
}
// if (!allowBFrames && M.hasBFrames(*trit)){problems = true;}
if (!allowBFrames && M.hasBFrames(*trit)){problems = true;}
if (problems){break;}
/*LTS-START*/
if (noSelAudio && Trk.type == "audio"){continue;}
if (noSelVideo && Trk.type == "video"){continue;}
if (noSelSub && (Trk.type == "subtitle" || Trk.type == "subtitle")){continue;}
if (noSelAudio && M.getType(*trit) == "audio"){continue;}
if (noSelVideo && M.getType(*trit) == "video"){continue;}
if (noSelSub &&
(M.getType(*trit) == "subtitle" || M.getCodec(*trit) == "subtitle")){
continue;
}
/*LTS-END*/
result.insert(*trit);
found = true;

View file

@ -9,8 +9,6 @@
#include "util.h"
#include <string>
const JSON::Value empty;
namespace Util{
void streamVariables(std::string &str, const std::string &streamname, const std::string &source = "");
std::string getTmpFolder();
@ -24,20 +22,17 @@ namespace Util{
JSON::Value getStreamConfig(const std::string &streamname);
JSON::Value getGlobalConfig(const std::string &optionName);
JSON::Value getInputBySource(const std::string &filename, bool isProvider = false);
DTSC::Meta getStreamMeta(const std::string &streamname);
uint8_t getStreamStatus(const std::string &streamname);
bool checkException(const JSON::Value &ex, const std::string &useragent);
std::string codecString(const std::string &codec, const std::string &initData = "");
std::set<size_t> getSupportedTracks(const DTSC::Meta &M, const JSON::Value &capa = empty,
std::set<size_t> getSupportedTracks(const DTSC::Meta &M, JSON::Value &capa,
const std::string &type = "", const std::string &UA = "");
std::set<size_t> findTracks(const DTSC::Meta &M, const JSON::Value &capa, const std::string &trackType,
const std::string &trackVal, const std::string &UA = "");
std::set<size_t> findTracks(const DTSC::Meta &M, const std::string &trackType, const std::string &trackVal);
std::set<size_t> wouldSelect(const DTSC::Meta &M, const std::string &trackSelector = "",
const JSON::Value &capa = empty, const std::string &UA = "");
JSON::Value capa = JSON::Value(), const std::string &UA = "");
std::set<size_t> wouldSelect(const DTSC::Meta &M, const std::map<std::string, std::string> &targetParams,
const JSON::Value &capa = empty, const std::string &UA = "",
uint64_t seekTarget = 0);
JSON::Value capa = JSON::Value(), const std::string &UA = "");
class DTSCShmReader{
public:

View file

@ -233,7 +233,7 @@ int stun_compute_hmac_sha1(uint8_t *message, uint32_t nbytes, std::string key, u
goto error;
}
DONTEVEN_MSG("Calculating hmac-sha1 with key `%s` with size %zu over %zu bytes of data.",
DONTEVEN_MSG("Calculating hmac-sha1 with key `%s` with size %zu over %" PRIu32 " bytes of data.",
key.c_str(), key.size(), nbytes);
r = mbedtls_md_hmac_starts(&md_ctx, (const unsigned char *)key.c_str(), key.size());

View file

@ -202,8 +202,10 @@ public:
/* write header and finalize. call for each stun message */
int begin(StunMessage &msg,
uint8_t paddingByte = 0x00); /* I've added the padding byte here so that we can use the
examples that can be found here https://tools.ietf.org/html/rfc5769#section-2.2
as they use 0x20 or 0x00 as the padding byte which is correct as you are free to use w/e padding byte you want. */
examples that can be found here
https://tools.ietf.org/html/rfc5769#section-2.2 as they
use 0x20 or 0x00 as the padding byte which is correct as
you are free to use w/e padding byte you want. */
int end();
/* write attributes */
@ -213,7 +215,9 @@ public:
int writeUsername(const std::string &username);
int writeSoftware(const std::string &software);
int writeMessageIntegrity(const std::string &password); /* When using WebRtc this is the ice-upwd of the other agent. */
int writeFingerprint(); /* Must be the last attribute in the message. When adding a fingerprint, make sure that it is added after the message-integrity (when you also use a message-integrity). */
int writeFingerprint(); /* Must be the last attribute in the message. When adding a fingerprint,
make sure that it is added after the message-integrity (when you also
use a message-integrity). */
/* get buffer */
uint8_t *getBufferPtr();

View file

@ -140,11 +140,10 @@ namespace Triggers{
return doTrigger(type, empty, streamName, true, usually_empty, paramsCB, extraParam);
}
///\brief handles triggers for a specific trigger event type, with a payload, for a specified stream, and/or server-wide
///\param type Trigger event type.
///\param payload Trigger type-specific data
///\param streamName The name of a stream.
///\returns Boolean, false if further processing should be aborted.
///\brief handles triggers for a specific trigger event type, with a payload, for a specified
/// stream, and/or server-wide \param type Trigger event type. \param payload Trigger
/// type-specific data \param streamName The name of a stream. \returns Boolean, false if further
/// processing should be aborted.
/// calls doTrigger with dryRun set to false
bool doTrigger(const std::string &type, const std::string &payload, const std::string &streamName){
usually_empty.clear();
@ -158,14 +157,17 @@ namespace Triggers{
///\param dryRun determines the mode of operation for this function
///\param response Returns the last received response by reference
///\returns Boolean, false if further processing should be aborted
/// This function attempts to open and parse a shared memory page with the config for a trigger event type, in order to parse the triggers
/// defined for that trigger event type.
/// The function can be used for two separate purposes, determined by the value of dryRun
///-if this function is called with dryRun==true (for example, from a handleTrigger function), the return value will be true, if at least one
/// trigger should be handled for the requested type/stream.
/// this can be used to make sure a payload is only generated if at least one trigger should be handled.
///-if this function is called with dryRun==false (for example, from one of the overloaded doTrigger functions), handleTrigger is called for
/// all configured triggers. In that case, the return value does not matter, it will probably be false in all cases.
/// This function attempts to open and parse a shared memory page with the config for a trigger
/// event type, in order to parse the triggers defined for that trigger event type. The function
/// can be used for two separate purposes, determined by the value of dryRun
///-if this function is called with dryRun==true (for example, from a handleTrigger function), the
/// return value will be true, if at least one trigger should be handled for the requested
/// type/stream.
/// this can be used to make sure a payload is only generated if at least one trigger should be
/// handled.
///-if this function is called with dryRun==false (for example, from one of the overloaded
/// doTrigger functions), handleTrigger is called for all configured triggers. In that case, the
/// return value does not matter, it will probably be false in all cases.
bool doTrigger(const std::string &type, const std::string &payload, const std::string &streamName,
bool dryRun, std::string &response, bool paramsCB(const char *, const void *),
const void *extraParam){

View file

@ -30,7 +30,38 @@
std::set<unsigned int> pmt_pids;
std::map<unsigned int, std::string> stream_pids;
/// A standard Program Association Table, as generated by FFMPEG.
/// Seems to be independent of the stream.
// 0x47 = sync byte
// 0x4000 = transport error(1) = 0, payload unit start(1) = 1, priority(1) = 0, PID(13) = 0
// 0x10 = transportscrambling(2) = 0, adaptation(2) = 1, continuity(4) = 0
// 0x00 = pointer = 0
// 0x00 = table ID = 0 = PAT
// 0xB00D = section syntax(1) = 1, 0(1)=0, reserved(2) = 3, section_len(12) = 13
// 0x0001 = transport stream id = 1
// 0xC1 = reserved(2) = 3, version(5)=0, curr_next_indi(1) = 1
// 0x00 = section_number = 0
// 0x00 = last_section_no = 0
// 0x0001 = ProgNo = 1
// 0xF000 = reserved(3) = 7, network pid = 4096
// 0x2AB104B2 = CRC32
namespace TS{
char PAT[188] ={
0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xB0, 0x0D, 0x00, 0x01, 0xC1, 0x00, 0x00, 0x00, 0x01,
0xF0, 0x00, 0x2A, 0xB1, 0x04, 0xB2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
/// This constructor creates an empty Packet, ready for use for either reading or writing.
/// All this constructor does is call Packet::clear().
Packet::Packet(){
@ -524,6 +555,35 @@ namespace TS{
}
return tmpStr;
}
/// Generates a PES Lead-in for a meta frame.
/// Prepends the lead-in to variable toSend, assumes toSend's length is all other data.
/// \param len The length of this frame.
/// \param PTS The timestamp of the frame.
std::string &Packet::getPESMetaLeadIn(unsigned int len, unsigned long long PTS, uint64_t bps){
if (bps >= 50){
len += 3;
}else{
bps = 0;
}
static std::string tmpStr;
tmpStr.clear();
tmpStr.reserve(20);
len += 8;
tmpStr.append("\000\000\001\374", 4);
tmpStr += (char)((len & 0xFF00) >> 8); // PES PacketLength
tmpStr += (char)(len & 0x00FF); // PES PacketLength (Cont)
tmpStr += (char)0x84; // isAligned
tmpStr += (char)(0x80 | (bps ? 0x10 : 0)); // PTS/DTS + Flags
tmpStr += (char)(5 + (bps ? 3 : 0)); // PESHeaderDataLength
encodePESTimestamp(tmpStr, 0x20, PTS);
if (bps){
char rate_buf[3];
Bit::htob24(rate_buf, (bps / 50) | 0x800001);
tmpStr.append(rate_buf, 3);
}
return tmpStr;
}
// END PES FUNCTIONS
/// Fills the free bytes of the Packet.
@ -1108,18 +1168,20 @@ namespace TS{
///\param selectedTracks tracks to include in PMT creation
///\param myMeta
///\returns character pointer to a static 188B TS packet
const char *createPMT(std::set<unsigned long> &selectedTracks, DTSC::Meta &myMeta, int contCounter){
const char *createPMT(std::set<unsigned long> &selectedTracks, const DTSC::Meta &M, int contCounter){
static ProgramMappingTable PMT;
PMT.setPID(4096);
PMT.setTableId(2);
// section length met 2 tracks: 0xB017
int sectionLen = 0;
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
std::string codec = M.getCodec(*it);
sectionLen += 5;
if (myMeta.tracks[*it].codec == "ID3"){sectionLen += myMeta.tracks[*it].init.size();}
if (myMeta.tracks[*it].codec == "AAC"){
if (codec == "ID3" || codec == "RAW"){sectionLen += M.getInit(*it).size();}
if (codec == "AAC"){
sectionLen += 4; // aac descriptor
if (myMeta.tracks[*it].lang.size() == 3 && myMeta.tracks[*it].lang != "und"){
std::string lang = M.getLang(*it);
if (lang.size() == 3 && lang != "und"){
sectionLen += 6; // language descriptor
}
}
@ -1133,42 +1195,51 @@ namespace TS{
PMT.setContinuityCounter(contCounter);
int vidTrack = -1;
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks[*it].type == "video"){
if (M.getType(*it) == "video"){
vidTrack = *it;
break;
}
}
if (vidTrack == -1){vidTrack = *(selectedTracks.begin());}
PMT.setPCRPID(255 + vidTrack);
size_t pcrPid = M.getID(vidTrack);
if (pcrPid < 255){pcrPid += 255;}
PMT.setPCRPID(pcrPid);
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(255 + *it);
std::string codec = M.getCodec(*it);
size_t pkgId = M.getID(*it);
if (pkgId < 255){pkgId += 255;}
entry.setElementaryPid(pkgId);
entry.setESInfo("");
if (myMeta.tracks[*it].codec == "H264"){
if (codec == "H264"){
entry.setStreamType(0x1B);
}else if (myMeta.tracks[*it].codec == "HEVC"){
}else if (codec == "HEVC"){
entry.setStreamType(0x24);
}else if (myMeta.tracks[*it].codec == "MPEG2"){
}else if (codec == "MPEG2"){
entry.setStreamType(0x02);
}else if (myMeta.tracks[*it].codec == "AAC"){
}else if (codec == "AAC"){
entry.setStreamType(0x0F);
std::string aac_info("\174\002\121\000", 4); // AAC descriptor: AAC Level 2. Hardcoded, because... what are AAC levels, anyway?
std::string aac_info("\174\002\121\000",
4); // AAC descriptor: AAC Level 2. Hardcoded, because... what are AAC levels, anyway?
// language code ddescriptor
if (myMeta.tracks[*it].lang.size() == 3 && myMeta.tracks[*it].lang != "und"){
std::string lang = M.getLang(*it);
if (lang.size() == 3 && lang != "und"){
aac_info.append("\012\004", 2);
aac_info.append(myMeta.tracks[*it].lang);
aac_info.append(lang);
aac_info.append("\000", 1);
}
entry.setESInfo(aac_info);
}else if (myMeta.tracks[*it].codec == "MP3" || myMeta.tracks[*it].codec == "MP2"){
}else if (codec == "MP3" || codec == "MP2"){
entry.setStreamType(0x03);
}else if (myMeta.tracks[*it].codec == "AC3"){
}else if (codec == "AC3"){
entry.setStreamType(0x81);
}else if (myMeta.tracks[*it].codec == "ID3"){
}else if (codec == "ID3"){
entry.setStreamType(0x15);
entry.setESInfo(myMeta.tracks[*it].init);
entry.setESInfo(M.getInit(*it));
}else if (codec == "RAW"){
entry.setStreamType(0x06);
entry.setESInfo(M.getInit(*it));
}
entry.advance();
}
@ -1365,7 +1436,9 @@ namespace TS{
getOffset() + getSectionLength();
unsigned int newVal; // this will hold the CRC32 value;
unsigned int pidLoc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 1;
newVal = checksum::crc32(-1, strBuf + pidLoc, loc - pidLoc); // calculating checksum over all the fields from table ID to the last stream element
newVal = checksum::crc32(-1, strBuf + pidLoc,
loc - pidLoc); // calculating checksum over all the fields from table
// ID to the last stream element
updPos(188);
strBuf[loc + 3] = (newVal >> 24) & 0xFF;
strBuf[loc + 2] = (newVal >> 16) & 0xFF;

View file

@ -75,6 +75,7 @@ namespace TS{
static std::string &getPESVideoLeadIn(unsigned int len, unsigned long long PTS,
unsigned long long offset, bool isAligned, uint64_t bps = 0);
static std::string &getPESAudioLeadIn(unsigned int len, unsigned long long PTS, uint64_t bps = 0);
static std::string &getPESMetaLeadIn(unsigned int len, unsigned long long PTS, uint64_t bps = 0);
// Printers and writers
std::string toPrettyString(size_t indent = 0, int detailLevel = 3) const;
@ -242,37 +243,9 @@ namespace TS{
return std::string(StandardHeader, 7);
}
/// A standard Program Association Table, as generated by FFMPEG.
/// Seems to be independent of the stream.
// 0x47 = sync byte
// 0x4000 = transport error(1) = 0, payload unit start(1) = 1, priority(1) = 0, PID(13) = 0
// 0x10 = transportscrambling(2) = 0, adaptation(2) = 1, continuity(4) = 0
// 0x00 = pointer = 0
// 0x00 = table ID = 0 = PAT
// 0xB00D = section syntax(1) = 1, 0(1)=0, reserved(2) = 3, section_len(12) = 13
// 0x0001 = transport stream id = 1
// 0xC1 = reserved(2) = 3, version(5)=0, curr_next_indi(1) = 1
// 0x00 = section_number = 0
// 0x00 = last_section_no = 0
// 0x0001 = ProgNo = 1
// 0xF000 = reserved(3) = 7, network pid = 4096
// 0x2AB104B2 = CRC32
static char PAT[188] ={
0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xB0, 0x0D, 0x00, 0x01, 0xC1, 0x00, 0x00, 0x00, 0x01,
0xF0, 0x00, 0x2A, 0xB1, 0x04, 0xB2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
extern char PAT[188];
const char *createPMT(std::set<unsigned long> &selectedTracks, DTSC::Meta &myMeta, int contCounter = 0);
const char *createPMT(std::set<unsigned long> &selectedTracks, const DTSC::Meta &M, int contCounter = 0);
const char *createSDT(const std::string &streamName, int contCounter = 0);
}// namespace TS

View file

@ -203,8 +203,9 @@ namespace TS{
case ID3:
case MP2:
case MPEG2:
case META:
pidToCodec[pid] = sType;
if (sType == ID3){
if (sType == ID3 || sType == META){
metaInit[pid] = std::string(entry.getESInfo(), entry.getESInfoLength());
}
break;
@ -536,7 +537,7 @@ namespace TS{
}
}
}
if (thisCodec == ID3 || thisCodec == AC3 || thisCodec == MP2){
if (thisCodec == ID3 || thisCodec == AC3 || thisCodec == MP2 || thisCodec == META){
out.push_back(DTSC::Packet());
out.back().genericFill(timeStamp, timeOffset, tid, pesPayload, realPayloadSize, bPos, 0);
if (thisCodec == MP2 && !mp2Hdr.count(tid)){
@ -607,7 +608,7 @@ namespace TS{
DTSC::Packet &bp = buildPacket[tid];
// Check if this is a keyframe
parseNal(tid, pesPayload, nextPtr, isKeyFrame);
parseNal(tid, pesPayload, pesPayload + nalSize, isKeyFrame);
// If yes, set the keyframe flag
if (isKeyFrame){bp.setKeyFrame(true);}
@ -651,10 +652,10 @@ namespace TS{
while (nextPtr < pesEnd && nalno < 8){
if (!nextPtr){nextPtr = pesEnd;}
// Calculate size of NAL unit, removing null bytes from the end
nalu::nalEndPosition(pesPayload, nextPtr - pesPayload);
uint32_t nalSize = nalu::nalEndPosition(pesPayload, nextPtr - pesPayload) - pesPayload;
// Check if this is a keyframe
parseNal(tid, pesPayload, nextPtr, isKeyFrame);
parseNal(tid, pesPayload, pesPayload + nalSize, isKeyFrame);
++nalno;
if (((nextPtr - pesPayload) + 3) >= realPayloadSize){break;}// end of the loop
@ -667,7 +668,7 @@ namespace TS{
}
}
void Stream::getPacket(size_t tid, DTSC::Packet &pack){
void Stream::getPacket(size_t tid, DTSC::Packet &pack, size_t mappedAs){
tthread::lock_guard<tthread::recursive_mutex> guard(tMutex);
pack.null();
if (!hasPacket(tid)){
@ -687,7 +688,7 @@ namespace TS{
return;
}
pack = outPackets[tid].front();
pack = DTSC::Packet(outPackets[tid].front(), mappedAs);
outPackets[tid].pop_front();
if (!outPackets[tid].size()){outPackets.erase(tid);}
@ -825,14 +826,18 @@ namespace TS{
void Stream::initializeMetadata(DTSC::Meta &meta, size_t tid, size_t mappingId){
tthread::lock_guard<tthread::recursive_mutex> guard(tMutex);
size_t mId = mappingId;
for (std::map<size_t, uint32_t>::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){
if (tid && it->first != tid){continue;}
if (tid != INVALID_TRACK_ID && it->first != tid){continue;}
if (mId == 0){mId = it->first;}
size_t mId = (mappingId == INVALID_TRACK_ID ? it->first : mappingId);
if (meta.tracks.count(mId) && meta.tracks[mId].codec.size()){continue;}
size_t idx = meta.trackIDToIndex(mId, getpid());
if (idx != INVALID_TRACK_ID && meta.getCodec(idx).size()){continue;}
// We now know we have to add a new track, OR the current track still needs it metadata set
bool addNewTrack = false;
std::string type, codec, init;
uint64_t width = 0, height = 0, fpks = 0, size = 0, rate = 0, channels = 0;
switch (it->second){
case H264:{
@ -840,15 +845,11 @@ namespace TS{
MEDIUM_MSG("Aborted meta fill for h264 track %lu: no SPS/PPS", it->first);
continue;
}
meta.tracks[mId].type = "video";
meta.tracks[mId].codec = "H264";
meta.tracks[mId].trackID = mId;
// First generate needed data
std::string tmpBuffer = spsInfo[it->first];
h264::sequenceParameterSet sps(spsInfo[it->first].data(), spsInfo[it->first].size());
h264::sequenceParameterSet sps(tmpBuffer.data(), tmpBuffer.size());
h264::SPSMeta spsChar = sps.getCharacteristics();
meta.tracks[mId].width = spsChar.width;
meta.tracks[mId].height = spsChar.height;
meta.tracks[mId].fpks = spsChar.fps * 1000;
MP4::AVCC avccBox;
avccBox.setVersion(1);
avccBox.setProfile(spsInfo[it->first][1]);
@ -858,103 +859,111 @@ namespace TS{
avccBox.setSPS(spsInfo[it->first]);
avccBox.setPPSCount(1);
avccBox.setPPS(ppsInfo[it->first]);
meta.tracks[mId].init = std::string(avccBox.payload(), avccBox.payloadSize());
// Then set all data for track
addNewTrack = true;
type = "video";
codec = "H264";
width = spsChar.width;
height = spsChar.height;
fpks = spsChar.fps * 1000;
init.assign(avccBox.payload(), avccBox.payloadSize());
}break;
case H265:{
if (!hevcInfo.count(it->first) || !hevcInfo[it->first].haveRequired()){
MEDIUM_MSG("Aborted meta fill for hevc track %lu: no info nal unit", it->first);
continue;
}
meta.tracks[mId].type = "video";
meta.tracks[mId].codec = "HEVC";
meta.tracks[mId].trackID = mId;
meta.tracks[mId].init = hevcInfo[it->first].generateHVCC();
addNewTrack = true;
type = "video";
codec = "HEVC";
init = hevcInfo[it->first].generateHVCC();
h265::metaInfo metaInfo = hevcInfo[it->first].getMeta();
meta.tracks[mId].width = metaInfo.width;
meta.tracks[mId].height = metaInfo.height;
meta.tracks[mId].fpks = metaInfo.fps * 1000;
int pmtCount = associationTable.getProgramCount();
for (int i = 0; i < pmtCount; i++){
int pid = associationTable.getProgramPID(i);
ProgramMappingEntry entry = mappingTable[pid].getEntry(0);
while (entry){
if (entry.getElementaryPid() == tid){
meta.tracks[mId].lang =
ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).getLanguage();
}
entry.advance();
}
}
width = metaInfo.width;
height = metaInfo.height;
fpks = metaInfo.fps * 1000;
}break;
case MPEG2:{
meta.tracks[mId].type = "video";
meta.tracks[mId].codec = "MPEG2";
meta.tracks[mId].trackID = mId;
meta.tracks[mId].init = std::string("\000\000\001", 3) + mpeg2SeqHdr[it->first] +
std::string("\000\000\001", 3) + mpeg2SeqExt[it->first];
Mpeg::MPEG2Info info = Mpeg::parseMPEG2Header(meta.tracks[mId].init);
meta.tracks[mId].width = info.width;
meta.tracks[mId].height = info.height;
meta.tracks[mId].fpks = info.fps * 1000;
addNewTrack = true;
type = "video";
codec = "MPEG2";
init = std::string("\000\000\001", 3) + mpeg2SeqHdr[it->first] +
std::string("\000\000\001", 3) + mpeg2SeqExt[it->first];
Mpeg::MPEG2Info info = Mpeg::parseMPEG2Header(init);
width = info.width;
height = info.height;
fpks = info.fps * 1000;
}break;
case ID3:{
meta.tracks[mId].type = "meta";
meta.tracks[mId].codec = "ID3";
meta.tracks[mId].trackID = mId;
meta.tracks[mId].init = metaInit[it->first];
addNewTrack = true;
type = "meta";
codec = "ID3";
init = metaInit[it->first];
}break;
case META:{
addNewTrack = true;
type = "meta";
codec = "RAW";
init = metaInit[it->first];
}break;
case AC3:{
meta.tracks[mId].type = "audio";
meta.tracks[mId].codec = "AC3";
meta.tracks[mId].trackID = mId;
meta.tracks[mId].size = 16;
///\todo Fix these 2 values
meta.tracks[mId].rate = 0;
meta.tracks[mId].channels = 0;
addNewTrack = true;
type = "audio";
codec = "AC3";
size = 16;
}break;
case MP2:{
meta.tracks[mId].type = "audio";
meta.tracks[mId].codec = "MP2";
meta.tracks[mId].trackID = mId;
addNewTrack = true;
Mpeg::MP2Info info = Mpeg::parseMP2Header(mp2Hdr[it->first]);
meta.tracks[mId].rate = info.sampleRate;
meta.tracks[mId].channels = info.channels;
///\todo Fix this value
meta.tracks[mId].size = 0;
type = "audio";
codec = (info.layer == 3 ? "MP3" : "MP2");
rate = info.sampleRate;
channels = info.channels;
}break;
case AAC:{
meta.tracks[mId].type = "audio";
meta.tracks[mId].codec = "AAC";
meta.tracks[mId].trackID = mId;
meta.tracks[mId].size = 16;
meta.tracks[mId].rate = adtsInfo[it->first].getFrequency();
meta.tracks[mId].channels = adtsInfo[it->first].getChannelCount();
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[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) |
((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3);
meta.tracks[mId].init = std::string(audioInit, 2);
addNewTrack = true;
init.resize(2);
init[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) |
((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1);
init[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) |
((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3);
type = "audio";
codec = "AAC";
size = 16;
rate = adtsInfo[it->first].getFrequency();
channels = adtsInfo[it->first].getChannelCount();
}break;
}
// Add track to meta here, if newTrack is set. Otherwise only re-initialize values
if (idx == INVALID_TRACK_ID){
if (!addNewTrack){return;}
idx = meta.addTrack();
}
meta.setType(idx, type);
meta.setCodec(idx, codec);
meta.setID(idx, mId);
if (init.size()){meta.setInit(idx, init);}
meta.setWidth(idx, width);
meta.setHeight(idx, height);
meta.setFpks(idx, fpks);
meta.setSize(idx, size);
meta.setRate(idx, rate);
meta.setChannels(idx, channels);
size_t pmtCount = associationTable.getProgramCount();
for (size_t i = 0; i < pmtCount; i++){
uint32_t pid = associationTable.getProgramPID(i);
ProgramMappingEntry entry = mappingTable[pid].getEntry(0);
while (entry){
if (entry.getElementaryPid() == tid){
meta.tracks[mId].lang =
ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).getLanguage();
meta.setLang(idx, ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).getLanguage());
}
entry.advance();
}
}
MEDIUM_MSG("Initialized track %lu as %s %s", it->first, meta.tracks[mId].codec.c_str(),
meta.tracks[mId].type.c_str());
MEDIUM_MSG("Initialized track %lu as %s %s", idx, codec.c_str(), type.c_str());
}
}
@ -983,7 +992,8 @@ namespace TS{
case AC3:
case ID3:
case MP2:
case MPEG2: result.insert(entry.getElementaryPid()); break;
case MPEG2:
case META: result.insert(entry.getElementaryPid()); break;
default: break;
}
entry.advance();

View file

@ -18,7 +18,8 @@ namespace TS{
H265 = 0x24,
ID3 = 0x15,
MPEG2 = 0x02,
MP2 = 0x03
MP2 = 0x03,
META = 0x06
};
class ADTSRemainder{
@ -57,10 +58,10 @@ namespace TS{
bool hasPacketOnEachTrack() const;
bool hasPacket(size_t tid) const;
bool hasPacket() const;
void getPacket(size_t tid, DTSC::Packet &pack);
void getPacket(size_t tid, DTSC::Packet &pack, size_t mappedAs = INVALID_TRACK_ID);
uint32_t getEarliestPID();
void getEarliestPacket(DTSC::Packet &pack);
void initializeMetadata(DTSC::Meta &meta, size_t tid = 0, size_t mappingId = 0);
void initializeMetadata(DTSC::Meta &meta, size_t tid = INVALID_TRACK_ID, size_t mappingId = INVALID_TRACK_ID);
void partialClear();
void clear();
void finish();

View file

@ -396,6 +396,7 @@ namespace Util{
void FieldAccX::set(const std::string &val, size_t recordNo){
char *place = src->getPointer(field, recordNo);
memcpy(place, val.data(), std::min((size_t)field.size, val.size()));
place[std::min((size_t)field.size - 1, val.size())] = 0;
}
/// If waitReady is true (default), waits for isReady() to return true in 50ms sleep increments.
@ -939,10 +940,12 @@ namespace Util{
}
FieldAccX RelAccX::getFieldAccX(const std::string &fName){
if (!fields.count(fName)){return FieldAccX();}
return FieldAccX(this, fields.at(fName));
}
RelAccXFieldData RelAccX::getFieldData(const std::string &fName) const{
if (!fields.count(fName)){return RelAccXFieldData();}
return fields.at(fName);
}
}// namespace Util

View file

@ -63,7 +63,11 @@ namespace Util{
uint8_t type;
uint32_t size;
uint32_t offset;
RelAccXFieldData(){}
RelAccXFieldData(){
type = 0;
size = 0;
offset = 0;
}
RelAccXFieldData(uint8_t t, uint32_t s, uint32_t o){
type = t;
size = s;