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

2
.gitignore vendored
View file

@ -37,6 +37,8 @@ libtool
server.html*
*.js.h
*.css.h
noffmpeg.h
noh264.h
.dirstamp
*.orig
*.lock

View file

@ -9,6 +9,8 @@ if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++98")
SET(SOURCE_DIR ${PROJECT_SOURCE_DIR})
SET(BINARY_DIR ${PROJECT_BINARY_DIR})
set( CMAKE_EXPORT_COMPILE_COMMANDS ON ) #For YCM support
@ -132,6 +134,7 @@ endif()
########################################
message("Builing release ${RELEASE} for version ${PACKAGE_VERSION} @ debug level ${DEBUG}")
add_definitions(-g -funsigned-char -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DDEBUG=${DEBUG} -DPACKAGE_VERSION=${PACKAGE_VERSION} -DRELEASE=${RELEASE})
add_definitions(-Wall -Wno-sign-compare -Wparentheses)
########################################
# MistLib - Header Files #
@ -145,6 +148,8 @@ set(libHeaders
lib/bitstream.h
lib/certificate.h
lib/checksum.h
lib/cmaf.h
lib/comms.h
lib/config.h
lib/defines.h
lib/dtls_srtp_handshake.h
@ -168,7 +173,6 @@ set(libHeaders
lib/nal.h
lib/ogg.h
lib/procs.h
lib/rijndael.h
lib/rtmpchunks.h
lib/rtp_fec.h
lib/rtp.h
@ -207,11 +211,12 @@ add_library (mist
lib/encode.cpp
lib/bitfields.cpp
lib/bitstream.cpp
lib/cmaf.cpp
lib/comms.cpp
lib/certificate.cpp
lib/config.cpp
lib/dtls_srtp_handshake.cpp
lib/dtsc.cpp
lib/dtscmeta.cpp
lib/encryption.cpp
lib/flv_tag.cpp
lib/h264.cpp
@ -230,7 +235,6 @@ add_library (mist
lib/nal.cpp
lib/ogg.cpp
lib/procs.cpp
lib/rijndael.cpp
lib/rtmpchunks.cpp
lib/rtp_fec.cpp
lib/rtp.cpp
@ -356,7 +360,8 @@ macro(makeUtil utilName utilFile)
)
endmacro()
makeUtil(Stats stats)
#makeUtil(Stats stats)
makeUtil(META meta)
makeUtil(RAX rax)
makeUtil(AMF amf)
makeUtil(Certbot certbot)
@ -365,6 +370,14 @@ if (DEFINED LOAD_BALANCE )
endif()
#LTS_END
add_executable(MistTranslateH264
src/analysers/h264_translate.cpp
${BINARY_DIR}/mist/.headers
)
target_link_libraries(MistTranslateH264
mist
)
########################################
# MistServer - Inputs #
########################################
@ -396,7 +409,6 @@ endmacro()
makeInput(HLS hls)
makeInput(DTSC dtsc)
makeInput(DTSCCrypt dtsccrypt)
makeInput(MP3 mp3)
makeInput(FLV flv)
if (DEFINED WITH_AV )
@ -432,6 +444,9 @@ macro(makeOutput outputName format)
if (";${ARGN};" MATCHES ";ts;")
SET(tsOutput src/output/output_ts_base.cpp)
endif()
if (";${ARGN};" MATCHES ";jpg;")
SET(tsOutput generated/noffmpeg.h generated/noh264.h)
endif()
add_executable(MistOut${outputName}
src/output/mist_out.cpp
src/output/output.cpp
@ -439,6 +454,7 @@ macro(makeOutput outputName format)
src/io.cpp
${httpOutput}
${tsOutput}
${mp4Output}
${BINARY_DIR}/mist/.headers
)
set_target_properties(MistOut${outputName}
@ -455,21 +471,23 @@ endmacro()
makeOutput(RTMP rtmp)
makeOutput(DTSC dtsc)
makeOutput(OGG progressive_ogg http)
makeOutput(FLV progressive_flv http)
makeOutput(OGG ogg http)
makeOutput(FLV flv http)
makeOutput(HTTPMinimalServer http_minimalserver http)
makeOutput(MP4 progressive_mp4 http)
makeOutput(MP3 progressive_mp3 http)
makeOutput(MP4 mp4 http)
makeOutput(MP3 mp3 http)
makeOutput(H264 h264 http)
makeOutput(HSS hss http)
makeOutput(HDS hds http)
makeOutput(SRT srt http)
makeOutput(JSON json http)
if (DEFINED WITH_JPG )
makeOutput(JPG jpg http jpg)
endif()
makeOutput(TS ts ts)
makeOutput(HTTPTS httpts http ts)
makeOutput(HLS hls http ts)
makeOutput(CMAF cmaf http)#LTS
makeOutput(EBML ebml)
makeOutput(Push push)#LTS
makeOutput(RTSP rtsp)#LTS
makeOutput(WAV wav)#LTS
makeOutput(WebRTC webrtc http)#LTS
@ -501,7 +519,6 @@ target_link_libraries(MistProcMKVExec mist)
if (NOT DEFINED NOSSL )
makeOutput(HTTPS https)#LTS
endif()
makeOutput(DASH dash_mp4 http)#LTS
if (DEFINED WITH_SANITY )
makeOutput(SanityCheck sanitycheck)#LTS
@ -586,6 +603,17 @@ else()
endif()
endif()
########################################
# RelAccX Sampler #
########################################
add_executable(RelAccXSampler
src/relaccxsampler.cpp
${BINARY_DIR}/mist/.headers
)
target_link_libraries(RelAccXSampler
mist
)
########################################
# Embed Code #
########################################
@ -658,6 +686,18 @@ add_custom_command(OUTPUT generated/skin_videojs.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/skins/video-js.css
)
########################################
# JPG output #
########################################
add_custom_command(OUTPUT generated/noffmpeg.h
COMMAND ./sourcery ${SOURCE_DIR}/src/output/noffmpeg.jpg noffmpeg generated/noffmpeg.h
DEPENDS sourcery ${SOURCE_DIR}/src/output/noffmpeg.jpg
)
add_custom_command(OUTPUT generated/noh264.h
COMMAND ./sourcery ${SOURCE_DIR}/src/output/noh264.jpg noh264 generated/noh264.h
DEPENDS sourcery ${SOURCE_DIR}/src/output/noh264.jpg
)
########################################
# Local Settings Page #
########################################
@ -742,15 +782,6 @@ add_custom_target(clean-all
########################################
# Tests #
########################################
add_executable(aes_ctr128
test/aes_ctr128.cpp
${BINARY_DIR}/mist/.headers
)
target_link_libraries(aes_ctr128
mist
)
add_test(AESTest COMMAND aes_ctr128)
add_executable(urltest test/url.cpp ${BINARY_DIR}/mist/.headers)
target_link_libraries(urltest mist)
add_test(URLTest COMMAND urltest)
@ -768,4 +799,7 @@ target_link_libraries(jsontest mist)
add_test(JSONTest COMMAND jsontest)
add_executable(resolvetest test/resolve.cpp ${BINARY_DIR}/mist/.headers)
target_link_libraries(resolvetest mist)
add_executable(bitwritertest test/bitwriter.cpp ${BINARY_DIR}/mist/.headers)
target_link_libraries(bitwritertest mist)
add_test(BitWriterTest COMMAND bitwritertest)

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 bitWriter::size(){return dataSize;}
size_t adjustableBits = (bitSize % 8) + bitLength;
size_t adjustableBytes = adjustableBits / 8 + (adjustableBits % 8 ? 1 : 0);
void bitWriter::append(uint64_t value, size_t bitLength){
if (dataSize + bitLength > bufferSize){reallocate(dataSize + bitLength);}
for (int i = 0; i < adjustableBytes; i++){p[bitSize / 8 + i] |= buf[i];}
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);
}
bitSize += bitLength;
}
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,7 +43,8 @@ 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::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;

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);
}
return res.str();
void AES::setDecryptKey(const char *key){
mbedtls_aes_setkey_dec(&ctx, (const unsigned char *)key, 128);
}
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);
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;
int pos = 0;
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;
}
}
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);
}
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]);}
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;
}
void fillVerimatrix(verimatrixData &vmData){
int hostPos = vmData.url.find("://") + 3;
int portPos = vmData.url.find(":", hostPos);
char *data;
size_t dataLen;
src.getString("data", data, dataLen);
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);
size_t trackIdx = M.getSourceTrack(newTrack);
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");
char *encData = (char *)malloc(dataLen);
size_t dataOffset = 0;
if (M.getType(trackIdx) == "video" && dataLen > 96){
dataOffset = dataLen - (int((dataLen - 96) / 16) * 16);
memcpy(encData, data, dataOffset);
}
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);
key = binKey(key);
if (!encryptBlockCTR(ivec, data + dataOffset, encData + dataOffset, dataLen - dataOffset)){
FAIL_MSG("Failed to encrypt packet");
free(encData);
return res;
}
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);
res.genericFill(src.getTime(), src.getInt("offset"), newTrack, encData, dataLen, 0, src.getFlag("keyframe"));
free(encData);
return res;
}
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;
}
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};
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;
}
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){
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: reTrack = 1; break; // video
case 0x08: reTrack = 2; break; // audio
case 0x12: reTrack = 3; break; // meta
}
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;
// 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);
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;
}
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());
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;
}
INFO_MSG("Selecting %s track %zd (%s/%s)", trackType.c_str(), trackNo, Trk.type.c_str(),
Trk.codec.c_str());
result.insert(trackNo);
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] +
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(meta.tracks[mId].init);
meta.tracks[mId].width = info.width;
meta.tracks[mId].height = info.height;
meta.tracks[mId].fpks = info.fps * 1000;
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) |
addNewTrack = true;
init.resize(2);
init[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) |
((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1);
audioInit[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) |
init[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) |
((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3);
meta.tracks[mId].init = std::string(audioInit, 2);
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;

View file

@ -67,10 +67,10 @@ int Analyser::run(Util::Config &conf){
if (validate && ((finTime - upTime + 10) * 1000 < mediaTime)){
uint32_t sleepMs = mediaTime - (Util::bootSecs() - upTime + 10) * 1000;
if ((finTime - upTime + sleepMs / 1000) >= timeOut){
WARN_MSG("Reached timeout of %llu seconds, stopping", timeOut);
WARN_MSG("Reached timeout of %" PRIu64 " seconds, stopping", timeOut);
return 3;
}
INFO_MSG("Sleeping for %lums", sleepMs);
INFO_MSG("Sleeping for %" PRIu32 "ms", sleepMs);
Util::sleep(sleepMs);
finTime = Util::bootSecs();
}
@ -80,7 +80,7 @@ int Analyser::run(Util::Config &conf){
return 4;
}
if ((finTime - upTime) >= timeOut){
WARN_MSG("Reached timeout of %llu seconds, stopping", timeOut);
WARN_MSG("Reached timeout of %" PRIu64 " seconds, stopping", timeOut);
return 3;
}
}

View file

@ -30,17 +30,14 @@ bool getDelimBlock(std::string &data, std::string name, size_t &blockStart, size
offset--;
blockStart = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockStart: %i ", offset, blockStart);
offset = blockStart + 1; // skip single character!
blockEnd = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockEnd: %i ", offset, blockEnd);
if (blockStart == std::string::npos || blockEnd == std::string::npos){
return false; // no start/end quotes found
}
blockEnd++; // include delim
// DEBUG_MSG(DLVL_INFO, "getDelimPos: data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart) );
return true;
}
@ -50,15 +47,12 @@ bool getValueBlock(std::string &data, std::string name, size_t &blockStart, size
return false; // name string not found.
}
blockStart = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockStart: %i ", offset, blockStart);
blockStart++; // clip off quote characters
offset = blockStart; // skip single character!
blockEnd = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockEnd: %i ", offset, blockEnd);
if (blockStart == std::string::npos || blockEnd == std::string::npos){
return false; // no start/end quotes found
}
// DEBUG_MSG(DLVL_INFO, "getValueBlock: data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart) );
return true;
}
@ -67,23 +61,17 @@ bool getString(std::string &data, std::string name, std::string &output){
size_t blockEnd = 0;
if (!getValueBlock(data, name, blockStart, blockEnd, "\"")){
// DEBUG_MSG(DLVL_FAIL, "could not find \"%s\" in data block", name.c_str());
return false; // could not find value in this data block.
}
// DEBUG_MSG(DLVL_INFO, "data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart) )
output = data.substr(blockStart, (blockEnd - blockStart));
// looks like this function is working as expected
// DEBUG_MSG(DLVL_INFO, "data in getstring %s", (data.substr(blockStart,(blockEnd-blockStart))).c_str());
return true;
}
bool getLong(std::string &data, std::string name, long &output){
size_t blockStart, blockEnd;
if (!getValueBlock(data, name, blockStart, blockEnd, "\"")){
// DEBUG_MSG(DLVL_FAIL, "could not find \"%s\" in data block", name.c_str());
return false; // could not find value in this data block.
}
// DEBUG_MSG(DLVL_INFO, "name: %s data in atol %s",name.c_str(), (data.substr(blockStart,(blockEnd-blockStart))).c_str());
output = atol((data.substr(blockStart, (blockEnd - blockStart))).c_str());
return true;
}
@ -96,7 +84,7 @@ bool getBlock(std::string &data, std::string name, int offset, size_t &blockStar
}
if (blockStart == std::string::npos){
DEBUG_MSG(DLVL_INFO, "no block start found for name: %s at offset: %i", name.c_str(), offset);
INFO_MSG("no block start found for name: %s at offset: %i", name.c_str(), offset);
return false;
}
@ -104,75 +92,71 @@ bool getBlock(std::string &data, std::string name, int offset, size_t &blockStar
if (blockEnd == std::string::npos){
blockEnd = data.find("/>", blockStart);
if (blockEnd == std::string::npos){
DEBUG_MSG(DLVL_INFO, "no block end found.");
INFO_MSG("no block end found.");
return false;
}
size_t temp = data.find("<", blockStart + 1, (blockEnd - blockStart - 1)); // the +1 is to avoid re-interpreting the starting < //TODO!!
size_t temp = data.find("<", blockStart + 1,
(blockEnd - blockStart - 1)); // the +1 is to avoid re-interpreting the starting < //TODO!!
if (temp != std::string::npos){// all info is epxected between <name ... />
DEBUG_MSG(DLVL_FAIL, "block start found before block end. offset: %lu block: %s", temp, data.c_str());
FAIL_MSG("block start found before block end. offset: %lu block: %s", temp, data.c_str());
return false;
}
// DEBUG_MSG(DLVL_FAIL, "special block end found");
blockEnd += 2; // position after />
}else{
blockEnd += name.size() + 2; // position after /name>
}
// DEBUG_MSG(DLVL_INFO, "getBlock: start: %i end: %i",blockStart,blockEnd);
return true;
}
bool parseAdaptationSet(std::string &data, std::set<seekPos> &currentPos){
// DEBUG_MSG(DLVL_INFO, "Parsing adaptationSet: %s", data.c_str());
size_t offset = 0;
size_t blockStart, blockEnd;
tempSD.trackType = OTHER;
// get value: mimetype //todo: handle this!
std::string mimeType;
if (!getString(
data, "mimeType",
if (!getString(data, "mimeType",
mimeType)){// get first occurence of mimeType. --> this will break if multiple mimetypes
// should be read from this block because no offset is provided. solution:
// use this on a substring containing the desired information.
DEBUG_MSG(DLVL_FAIL, "mimeType not found");
FAIL_MSG("mimeType not found");
return false;
}
DEBUG_MSG(DLVL_INFO, "mimeType: %s", mimeType.c_str()); // checked, OK
INFO_MSG("mimeType: %s", mimeType.c_str()); // checked, OK
if (mimeType.find("video") != std::string::npos){tempSD.trackType = VIDEO;}
if (mimeType.find("audio") != std::string::npos){tempSD.trackType = AUDIO;}
if (tempSD.trackType == OTHER){
DEBUG_MSG(DLVL_FAIL, "no audio or video type found. giving up.");
FAIL_MSG("no audio or video type found. giving up.");
return false;
}
// find an ID within this adaptationSet block.
if (!getBlock(data, (std::string) "Representation", offset, blockStart, blockEnd)){
DEBUG_MSG(DLVL_FAIL, "Representation not found");
FAIL_MSG("Representation not found");
return false;
}
// representation string
std::string block = data.substr(blockStart, (blockEnd - blockStart));
DEBUG_MSG(DLVL_INFO, "Representation block: %s", block.c_str());
// check if block is not junk?
INFO_MSG("Representation block: %s", block.c_str());
///\todo check if block is not junk?
if (!getLong(block, "id", tempSD.trackID)){
DEBUG_MSG(DLVL_FAIL, "Representation id not found in block %s", block.c_str());
FAIL_MSG("Representation id not found in block %s", block.c_str());
return false;
}
DEBUG_MSG(DLVL_INFO, "Representation/id: %li", tempSD.trackID); // checked, OK
INFO_MSG("Representation/id: %li", tempSD.trackID); // checked, OK
offset = 0;
// get values from SegmentTemplate
if (!getBlock(data, (std::string) "SegmentTemplate", offset, blockStart, blockEnd)){
DEBUG_MSG(DLVL_FAIL, "SegmentTemplate not found");
FAIL_MSG("SegmentTemplate not found");
return false;
}
block = data.substr(blockStart, (blockEnd - blockStart));
// DEBUG_MSG(DLVL_INFO, "SegmentTemplate block: %s",block.c_str()); //OK
getLong(block, "timescale", tempSD.timeScale);
getString(block, "media", tempSD.media);
@ -181,20 +165,19 @@ bool parseAdaptationSet(std::string &data, std::set<seekPos> &currentPos){
size_t tmpBlockStart = 0;
size_t tmpBlockEnd = 0;
if (!getDelimBlock(tempSD.media, "RepresentationID", tmpBlockStart, tmpBlockEnd, "$")){
DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $RepresentationID$ in %s", tempSD.media.c_str());
FAIL_MSG("Failed to find and replace $RepresentationID$ in %s", tempSD.media.c_str());
return false;
}
tempSD.media.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
if (!getDelimBlock(tempSD.media, "Time", tmpBlockStart, tmpBlockEnd, "$")){
DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $Time$ in %s", tempSD.media.c_str());
FAIL_MSG("Failed to find and replace $Time$ in %s", tempSD.media.c_str());
return false;
}
tempSD.media.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
if (!getDelimBlock(tempSD.initialization, "RepresentationID", tmpBlockStart, tmpBlockEnd, "$")){
DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $RepresentationID$ in %s",
tempSD.initialization.c_str());
FAIL_MSG("Failed to find and replace $RepresentationID$ in %s", tempSD.initialization.c_str());
return false;
}
tempSD.initialization.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
@ -202,12 +185,12 @@ bool parseAdaptationSet(std::string &data, std::set<seekPos> &currentPos){
// get segment timeline block from within segment template:
size_t blockOffset = 0; // offset should be 0 because this is a new block
if (!getBlock(block, "SegmentTimeline", blockOffset, blockStart, blockEnd)){
DEBUG_MSG(DLVL_FAIL, "SegmentTimeline block not found");
FAIL_MSG("SegmentTimeline block not found");
return false;
}
std::string block2 = block.substr(blockStart, (blockEnd - blockStart)); // overwrites previous block (takes just the segmentTimeline part
// DEBUG_MSG(DLVL_INFO, "SegmentTimeline block: %s",block2.c_str()); //OK
std::string block2 = block.substr(blockStart,
(blockEnd - blockStart)); // overwrites previous block (takes just the segmentTimeline part
int numS = 0;
offset = 0;
@ -216,10 +199,10 @@ bool parseAdaptationSet(std::string &data, std::set<seekPos> &currentPos){
while (1){
if (!getBlock(block2, "S", offset, blockStart, blockEnd)){
if (numS == 0){
DEBUG_MSG(DLVL_FAIL, "no S found within SegmentTimeline");
FAIL_MSG("no S found within SegmentTimeline");
return false;
}else{
DEBUG_MSG(DLVL_INFO, "all S found within SegmentTimeline %i", numS);
INFO_MSG("all S found within SegmentTimeline %i", numS);
return true; // break; //escape from while loop (to return true)
}
}
@ -227,16 +210,14 @@ bool parseAdaptationSet(std::string &data, std::set<seekPos> &currentPos){
// stuff S data into: currentPos
// searching for t(start position)
std::string sBlock = block2.substr(blockStart, (blockEnd - blockStart));
// DEBUG_MSG(DLVL_INFO, "S found. offset: %i blockStart: %i blockend: %i block: %s",offset,blockStart, blockEnd, sBlock.c_str()); //OK!
if (getLong(sBlock, "t", timeValue)){
totalDuration = timeValue; // reset totalDuration to value of t
}
if (!getLong(sBlock, "d", timeValue)){// expected duration in every S.
DEBUG_MSG(DLVL_FAIL, "no d found within S");
FAIL_MSG("no d found within S");
return false;
}
// stuff data with old value (start of block)
// DEBUG_MSG(DLVL_INFO, "stuffing info from S into set");
seekPos thisPos;
thisPos.trackType = tempSD.trackType;
thisPos.trackID = tempSD.trackID;
@ -249,7 +230,6 @@ bool parseAdaptationSet(std::string &data, std::set<seekPos> &currentPos){
static char charBuf[512];
snprintf(charBuf, 512, tempSD.media.c_str(), tempSD.trackID, totalDuration);
thisPos.url.assign(charBuf);
// DEBUG_MSG(DLVL_INFO, "media url (from rep.ID %d, startTime %d): %s", tempSD.trackID, totalDuration,thisPos.url.c_str());
currentPos.insert(thisPos); // assumes insert copies all data in seekPos struct.
totalDuration += timeValue; // update totalDuration
@ -265,30 +245,30 @@ bool parseXML(std::string &body, std::set<seekPos> &currentPos, std::vector<Stre
size_t currentOffset = 0;
size_t adaptationSetStart;
size_t adaptationSetEnd;
// DEBUG_MSG(DLVL_INFO, "body received: %s", body.c_str());
while (getBlock(body, "AdaptationSet", currentOffset, adaptationSetStart, adaptationSetEnd)){
tempSD.adaptationSet = numAdaptationSet;
numAdaptationSet++;
DEBUG_MSG(DLVL_INFO, "adaptationSet found. start: %lu end: %lu num: %lu ", adaptationSetStart,
INFO_MSG("adaptationSet found. start: %lu end: %lu num: %lu ", adaptationSetStart,
adaptationSetEnd, (adaptationSetEnd - adaptationSetStart));
// get substring: from <adaptationSet... to /adaptationSet>
std::string adaptationSet = body.substr(adaptationSetStart, (adaptationSetEnd - adaptationSetStart));
// function was verified: output as expected.
if (!parseAdaptationSet(adaptationSet, currentPos)){
DEBUG_MSG(DLVL_FAIL, "parseAdaptationSet returned false."); // this also happens in the case of OTHER mimetype. in that case it might be
// desirable to continue searching for valid data instead of quitting.
FAIL_MSG("parseAdaptationSet returned false."); // this also happens in the case of OTHER mimetype.
// in that case it might be desirable to continue
// searching for valid data instead of quitting.
return false;
}
streamData.push_back(tempSD); // put temp values into adaptation set vector
currentOffset = adaptationSetEnd; // the getblock function should make sure End is at the correct offset.
}
if (numAdaptationSet == 0){
DEBUG_MSG(DLVL_FAIL, "no adaptationSet found.");
FAIL_MSG("no adaptationSet found.");
return false;
}
DEBUG_MSG(DLVL_INFO, "all adaptation sets found. total: %i", numAdaptationSet);
INFO_MSG("all adaptation sets found. total: %i", numAdaptationSet);
return true;
}
@ -297,7 +277,7 @@ dashAnalyser::dashAnalyser(Util::Config conf) : analysers(conf){
url = conf.getString("url");
if (url.substr(0, 7) != "http://"){
DEBUG_MSG(DLVL_FAIL, "The URL must start with http://");
FAIL_MSG("The URL must start with http://");
// return -1;
exit(1);
}
@ -329,9 +309,9 @@ dashAnalyser::dashAnalyser(Util::Config conf) : analysers(conf){
}
// url:
DEBUG_MSG(DLVL_INFO, "url %s server: %s port: %d", url.c_str(), server.c_str(), port);
INFO_MSG("url %s server: %s port: %d", url.c_str(), server.c_str(), port);
urlPrependStuff = url.substr(0, url.rfind("/") + 1);
DEBUG_MSG(DLVL_INFO, "prepend stuff: %s", urlPrependStuff.c_str());
INFO_MSG("prepend stuff: %s", urlPrependStuff.c_str());
if (!conn){conn.open(server, port, false);}
pos = 0;
@ -346,13 +326,8 @@ dashAnalyser::dashAnalyser(Util::Config conf) : analysers(conf){
currentPos;
streamData;
// DEBUG_MSG(DLVL_INFO, "body received: %s", H.body.c_str()); //keeps giving empty stuff :(
// DEBUG_MSG(DLVL_INFO, "url %s ", url.c_str());
// std::ifstream in(url.c_str());
// std::string s((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
if (!parseXML(H.body, currentPos, streamData)){
DEBUG_MSG(DLVL_FAIL, "Manifest parsing failed. body: \n %s", H.body.c_str());
FAIL_MSG("Manifest parsing failed. body: \n %s", H.body.c_str());
if (conf.getString("mode") == "validate"){
long long int endTime = Util::bootSecs();
std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
@ -362,30 +337,30 @@ dashAnalyser::dashAnalyser(Util::Config conf) : analysers(conf){
}
H.Clean();
DEBUG_MSG(DLVL_INFO, "*********");
DEBUG_MSG(DLVL_INFO, "*SUMMARY*");
DEBUG_MSG(DLVL_INFO, "*********");
INFO_MSG("*********");
INFO_MSG("*SUMMARY*");
INFO_MSG("*********");
DEBUG_MSG(DLVL_INFO, "num streams: %lu", streamData.size());
INFO_MSG("num streams: %lu", streamData.size());
for (unsigned int i = 0; i < streamData.size(); i++){
DEBUG_MSG(DLVL_INFO, "");
DEBUG_MSG(DLVL_INFO, "ID in vector %d", i);
DEBUG_MSG(DLVL_INFO, "trackID %ld", streamData[i].trackID);
DEBUG_MSG(DLVL_INFO, "adaptationSet %d", streamData[i].adaptationSet);
DEBUG_MSG(DLVL_INFO, "trackType (audio 0x02, video 0x01) %d", streamData[i].trackType);
DEBUG_MSG(DLVL_INFO, "TimeScale %ld", streamData[i].timeScale);
DEBUG_MSG(DLVL_INFO, "Media string %s", streamData[i].media.c_str());
DEBUG_MSG(DLVL_INFO, "Init string %s", streamData[i].initialization.c_str());
INFO_MSG("");
INFO_MSG("ID in vector %d", i);
INFO_MSG("trackID %ld", streamData[i].trackID);
INFO_MSG("adaptationSet %d", streamData[i].adaptationSet);
INFO_MSG("trackType (audio 0x02, video 0x01) %d", streamData[i].trackType);
INFO_MSG("TimeScale %ld", streamData[i].timeScale);
INFO_MSG("Media string %s", streamData[i].media.c_str());
INFO_MSG("Init string %s", streamData[i].initialization.c_str());
}
DEBUG_MSG(DLVL_INFO, "");
INFO_MSG("");
for (unsigned int i = 0; i < streamData.size(); i++){// get init url
static char charBuf[512];
snprintf(charBuf, 512, streamData[i].initialization.c_str(), streamData[i].trackID);
streamData[i].initURL.assign(charBuf);
DEBUG_MSG(DLVL_INFO, "init url for adaptationSet %d trackID %ld: %s ",
streamData[i].adaptationSet, streamData[i].trackID, streamData[i].initURL.c_str());
INFO_MSG("init url for adaptationSet %d trackID %ld: %s ", streamData[i].adaptationSet,
streamData[i].trackID, streamData[i].initURL.c_str());
}
}
@ -403,8 +378,7 @@ dashAnalyser::~dashAnalyser(){
int dashAnalyser::doAnalyse(){
// DEBUG_MSG(DLVL_INFO, "next url: %s", currentPos.begin()->url.c_str());
// match adaptation set and track id?
///\todo match adaptation set and track id?
int tempID = 0;
for (unsigned int i = 0; i < streamData.size(); i++){
if (streamData[i].trackID == currentPos.begin()->trackID &&
@ -415,20 +389,16 @@ int dashAnalyser::doAnalyse(){
HTTP::Parser H;
H.url = urlPrependStuff;
H.url.append(currentPos.begin()->url);
DEBUG_MSG(DLVL_INFO, "Retrieving segment: %s (%llu-%llu)", H.url.c_str(),
currentPos.begin()->seekTime, currentPos.begin()->seekTime + currentPos.begin()->duration);
INFO_MSG("Retrieving segment: %s (%llu-%llu)", H.url.c_str(), currentPos.begin()->seekTime,
currentPos.begin()->seekTime + currentPos.begin()->duration);
H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString()); // wut?
H.SendRequest(conn);
// TODO: get response?
H.Clean();
while (conn && (!conn.spool() || !H.Read(conn))){}// ehm...
// std::cout << "leh vomi: "<<H.body <<std::endl;
// DEBUG_MSG(DLVL_INFO, "zut: %s", H.body.c_str());
// strBuf[tempID].append(H.body);
if (!H.body.size()){
DEBUG_MSG(DLVL_FAIL, "No data downloaded from %s", H.url.c_str());
// break;
FAIL_MSG("No data downloaded from %s", H.url.c_str());
return 0;
}
@ -440,17 +410,14 @@ int dashAnalyser::doAnalyse(){
}
if (!mdatSeen){
DEBUG_MSG(DLVL_FAIL, "No mdat present. Sadface. :-(");
// break;
FAIL_MSG("No mdat present. Sadface. :-(");
return 0;
}
if (H.body.size()){
DEBUG_MSG(DLVL_FAIL, "%lu bytes left in body. Assuming horrible things...", H.body.size()); //,H.body.c_str());
FAIL_MSG("%lu bytes left in body. Assuming horrible things...",
H.body.size()); //,H.body.c_str());
std::cerr << H.body << std::endl;
if (beforeParse == H.body.size()){
// break;
return 0;
}
if (beforeParse == H.body.size()){return 0;}
}
H.Clean();
@ -516,7 +483,7 @@ int main2(int argc, char **argv){
std::string url = conf.getString("url");
if (url.substr(0, 7) != "http://"){
DEBUG_MSG(DLVL_FAIL, "The URL must start with http://");
FAIL_MSG("The URL must start with http://");
return -1;
}
url = url.substr(7); // found problem if url is to short!! it gives out of range when entering http://meh.meh
@ -535,9 +502,9 @@ int main2(int argc, char **argv){
Socket::Connection conn(server, port, false);
// url:
DEBUG_MSG(DLVL_INFO, "url %s server: %s port: %d", url.c_str(), server.c_str(), port);
INFO_MSG("url %s server: %s port: %d", url.c_str(), server.c_str(), port);
std::string urlPrependStuff = url.substr(0, url.rfind("/") + 1);
DEBUG_MSG(DLVL_INFO, "prepend stuff: %s", urlPrependStuff.c_str());
INFO_MSG("prepend stuff: %s", urlPrependStuff.c_str());
if (!conn){conn.open(server, port, false);}
unsigned int pos = 0;
HTTP::Parser H;
@ -551,13 +518,8 @@ int main2(int argc, char **argv){
std::set<seekPos> currentPos;
std::vector<StreamData> streamData;
// DEBUG_MSG(DLVL_INFO, "body received: %s", H.body.c_str()); //keeps giving empty stuff :(
// DEBUG_MSG(DLVL_INFO, "url %s ", url.c_str());
// std::ifstream in(url.c_str());
// std::string s((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
if (!parseXML(H.body, currentPos, streamData)){
DEBUG_MSG(DLVL_FAIL, "Manifest parsing failed. body: \n %s", H.body.c_str());
FAIL_MSG("Manifest parsing failed. body: \n %s", H.body.c_str());
if (conf.getString("mode") == "validate"){
long long int endTime = Util::bootSecs();
std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
@ -566,37 +528,36 @@ int main2(int argc, char **argv){
}
H.Clean();
DEBUG_MSG(DLVL_INFO, "*********");
DEBUG_MSG(DLVL_INFO, "*SUMMARY*");
DEBUG_MSG(DLVL_INFO, "*********");
INFO_MSG("*********");
INFO_MSG("*SUMMARY*");
INFO_MSG("*********");
DEBUG_MSG(DLVL_INFO, "num streams: %lu", streamData.size());
INFO_MSG("num streams: %lu", streamData.size());
for (unsigned int i = 0; i < streamData.size(); i++){
DEBUG_MSG(DLVL_INFO, "");
DEBUG_MSG(DLVL_INFO, "ID in vector %d", i);
DEBUG_MSG(DLVL_INFO, "trackID %ld", streamData[i].trackID);
DEBUG_MSG(DLVL_INFO, "adaptationSet %d", streamData[i].adaptationSet);
DEBUG_MSG(DLVL_INFO, "trackType (audio 0x02, video 0x01) %d", streamData[i].trackType);
DEBUG_MSG(DLVL_INFO, "TimeScale %ld", streamData[i].timeScale);
DEBUG_MSG(DLVL_INFO, "Media string %s", streamData[i].media.c_str());
DEBUG_MSG(DLVL_INFO, "Init string %s", streamData[i].initialization.c_str());
INFO_MSG("");
INFO_MSG("ID in vector %d", i);
INFO_MSG("trackID %ld", streamData[i].trackID);
INFO_MSG("adaptationSet %d", streamData[i].adaptationSet);
INFO_MSG("trackType (audio 0x02, video 0x01) %d", streamData[i].trackType);
INFO_MSG("TimeScale %ld", streamData[i].timeScale);
INFO_MSG("Media string %s", streamData[i].media.c_str());
INFO_MSG("Init string %s", streamData[i].initialization.c_str());
}
DEBUG_MSG(DLVL_INFO, "");
INFO_MSG("");
for (unsigned int i = 0; i < streamData.size(); i++){// get init url
static char charBuf[512];
snprintf(charBuf, 512, streamData[i].initialization.c_str(), streamData[i].trackID);
streamData[i].initURL.assign(charBuf);
DEBUG_MSG(DLVL_INFO, "init url for adaptationSet %d trackID %ld: %s ",
streamData[i].adaptationSet, streamData[i].trackID, streamData[i].initURL.c_str());
INFO_MSG("init url for adaptationSet %d trackID %ld: %s ", streamData[i].adaptationSet,
streamData[i].trackID, streamData[i].initURL.c_str());
}
while (currentPos.size() && (abortTime <= 0 || Util::bootSecs() < startTime + abortTime)){
// DEBUG_MSG(DLVL_INFO, "next url: %s", currentPos.begin()->url.c_str());
std::cout << "blaa" << std::endl;
// match adaptation set and track id?
///\todo match adaptation set and track id?
int tempID = 0;
for (unsigned int i = 0; i < streamData.size(); i++){
if (streamData[i].trackID == currentPos.begin()->trackID &&
@ -607,18 +568,15 @@ int main2(int argc, char **argv){
HTTP::Parser H;
H.url = urlPrependStuff;
H.url.append(currentPos.begin()->url);
DEBUG_MSG(DLVL_INFO, "Retrieving segment: %s (%llu-%llu)", H.url.c_str(),
currentPos.begin()->seekTime, currentPos.begin()->seekTime + currentPos.begin()->duration);
INFO_MSG("Retrieving segment: %s (%llu-%llu)", H.url.c_str(), currentPos.begin()->seekTime,
currentPos.begin()->seekTime + currentPos.begin()->duration);
H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString()); // wut?
H.SendRequest(conn);
// TODO: get response?
H.Clean();
while (conn && (!conn.spool() || !H.Read(conn))){}// ehm...
// std::cout << "leh vomi: "<<H.body <<std::endl;
// DEBUG_MSG(DLVL_INFO, "zut: %s", H.body.c_str());
// strBuf[tempID].append(H.body);
if (!H.body.size()){
DEBUG_MSG(DLVL_FAIL, "No data downloaded from %s", H.url.c_str());
FAIL_MSG("No data downloaded from %s", H.url.c_str());
break;
}
size_t beforeParse = H.body.size();
@ -628,11 +586,12 @@ int main2(int argc, char **argv){
if (mp4Data.isType("mdat")){mdatSeen = true;}
}
if (!mdatSeen){
DEBUG_MSG(DLVL_FAIL, "No mdat present. Sadface. :-(");
FAIL_MSG("No mdat present. Sadface. :-(");
break;
}
if (H.body.size()){
DEBUG_MSG(DLVL_FAIL, "%lu bytes left in body. Assuming horrible things...", H.body.size()); //,H.body.c_str());
FAIL_MSG("%lu bytes left in body. Assuming horrible things...",
H.body.size()); //,H.body.c_str());
std::cerr << H.body << std::endl;
if (beforeParse == H.body.size()){break;}
}

View file

@ -35,7 +35,7 @@ bool AnalyserDTSC::parsePacket(){
}
P.reInit(conn);
if (conn && !P){
FAIL_MSG("Invalid DTSC packet @ byte %llu", totalBytes)
FAIL_MSG("Invalid DTSC packet @ byte %" PRIu64, totalBytes)
return false;
}
if (!conn && !P){
@ -70,8 +70,7 @@ bool AnalyserDTSC::parsePacket(){
case DTSC::DTSC_HEAD:{
if (detail >= 3){
std::cout << "DTSC header: ";
DTSC::Meta(P).toPrettyString(
std::cout, 0, (detail == 3 ? 0 : (detail == 4 ? 0x01 : (detail == 5 ? 0x03 : 0x07))));
std::cout << DTSC::Meta("", P.getScan()).toPrettyString();
}
if (detail == 2){std::cout << "DTSC header: " << P.getScan().toPrettyString() << std::endl;}
if (detail == 1){
@ -79,30 +78,34 @@ bool AnalyserDTSC::parsePacket(){
bool hasAAC = false;
JSON::Value result;
std::stringstream issues;
DTSC::Meta M(P);
for (std::map<unsigned int, DTSC::Track>::iterator it = M.tracks.begin(); it != M.tracks.end(); it++){
DTSC::Meta M("", P.getScan());
std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
std::string codec = M.getCodec(*it);
JSON::Value track;
track["kbits"] = (uint64_t)((double)it->second.bps * 8 / 1024);
track["codec"] = it->second.codec;
track["kbits"] = M.getBps(*it) * 8 / 1024;
track["codec"] = codec;
uint32_t shrtest_key = 0xFFFFFFFFul;
uint32_t longest_key = 0;
uint32_t shrtest_prt = 0xFFFFFFFFul;
uint32_t longest_prt = 0;
uint32_t shrtest_cnt = 0xFFFFFFFFul;
uint32_t longest_cnt = 0;
for (std::deque<DTSC::Key>::iterator k = it->second.keys.begin(); k != it->second.keys.end(); k++){
if (!k->getLength()){continue;}
if (k->getLength() > longest_key){longest_key = k->getLength();}
if (k->getLength() < shrtest_key){shrtest_key = k->getLength();}
if (k->getParts() > longest_cnt){longest_cnt = k->getParts();}
if (k->getParts() < shrtest_cnt){shrtest_cnt = k->getParts();}
if (k->getParts()){
if ((k->getLength() / k->getParts()) > longest_prt){
longest_prt = (k->getLength() / k->getParts());
}
if ((k->getLength() / k->getParts()) < shrtest_prt){
shrtest_prt = (k->getLength() / k->getParts());
}
DTSC::Keys keys(M.keys(*it));
uint32_t firstKey = keys.getFirstValid();
uint32_t endKey = keys.getEndValid();
for (int i = firstKey; i < endKey; i++){
uint64_t keyDuration = keys.getDuration(i);
uint64_t keyParts = keys.getParts(i);
if (!keyDuration){continue;}
if (keyDuration > longest_key){longest_key = keyDuration;}
if (keyDuration < shrtest_key){shrtest_key = keyDuration;}
if (keyParts > longest_cnt){longest_cnt = keyParts;}
if (keyParts < shrtest_cnt){shrtest_cnt = keyParts;}
if (keyParts){
if ((keyDuration / keyParts) > longest_prt){longest_prt = (keyDuration / keyParts);}
if ((keyDuration / keyParts) < shrtest_prt){shrtest_prt = (keyDuration / keyParts);}
}
}
track["keys"]["ms_min"] = shrtest_key;
@ -112,28 +115,28 @@ bool AnalyserDTSC::parsePacket(){
track["keys"]["frames_min"] = shrtest_cnt;
track["keys"]["frames_max"] = longest_cnt;
if (longest_prt > 500){
issues << "unstable connection (" << longest_prt << "ms " << it->second.codec << " frame)! ";
issues << "unstable connection (" << longest_prt << "ms " << codec << " frame)! ";
}
if (shrtest_cnt < 6){
issues << "unstable connection (" << shrtest_cnt << " " << it->second.codec << " frames in key)! ";
issues << "unstable connection (" << shrtest_cnt << " " << codec << " frames in key)! ";
}
if (it->second.codec == "AAC"){hasAAC = true;}
if (it->second.codec == "H264"){hasH264 = true;}
if (it->second.type == "video"){
track["width"] = it->second.width;
track["height"] = it->second.height;
track["fpks"] = it->second.fpks;
if (it->second.codec == "H264"){
if (codec == "AAC"){hasAAC = true;}
if (codec == "H264"){hasH264 = true;}
if (M.getType(*it) == "video"){
track["width"] = M.getWidth(*it);
track["height"] = M.getHeight(*it);
track["fpks"] = M.getFpks(*it);
if (codec == "H264"){
h264::sequenceParameterSet sps;
sps.fromDTSCInit(it->second.init);
sps.fromDTSCInit(M.getInit(*it));
h264::SPSMeta spsData = sps.getCharacteristics();
track["h264"]["profile"] = spsData.profile;
track["h264"]["level"] = spsData.level;
}
}
result[it->second.getWritableIdentifier()] = track;
result[M.getTrackIdentifier(*it)] = track;
}
if ((hasAAC || hasH264) && M.tracks.size() > 1){
if (hasAAC || hasH264){
if (!hasAAC){issues << "HLS no audio!";}
if (!hasH264){issues << "HLS no video!";}
}
@ -147,7 +150,7 @@ bool AnalyserDTSC::parsePacket(){
if (detail >= 2){std::cout << "DTCM command: " << P.getScan().toPrettyString() << std::endl;}
break;
}
default: FAIL_MSG("Invalid DTSC packet @ byte %llu", totalBytes); break;
default: FAIL_MSG("Invalid DTSC packet @ byte %" PRIu64, totalBytes); break;
}
totalBytes += P.getDataLen();

View file

@ -35,7 +35,7 @@ bool AnalyserEBML::parsePacket(){
if (dataBuffer.size() < neededBytes()){return false;}
EBML::Element E(dataBuffer.data(), true);
HIGH_MSG("Read an element at position %d", prePos);
HIGH_MSG("Read an element at position %zu", prePos);
if (detail >= 2){std::cout << E.toPrettyString(depthStash.size() * 2, detail);}
switch (E.getID()){
case EBML::EID_SEGMENT:

View file

@ -11,7 +11,7 @@ private:
uint64_t neededBytes();
std::string dataBuffer;
uint64_t curPos;
uint64_t prePos;
size_t prePos;
uint64_t segmentOffset;
uint32_t lastSeekId;
uint64_t lastSeekPos;

View file

@ -32,7 +32,8 @@ bool AnalyserFLV::parsePacket(){
// If we arrive here, we've loaded a FLV packet
if (!filter || filter == flvData.data[0]){
DETAIL_MED("[%llu+%llu] %s", flvData.tagTime(), flvData.offset(), flvData.tagType().c_str());
DETAIL_MED("[%" PRIu64 "+%" PRId64 "] %s", flvData.tagTime(), flvData.offset(),
flvData.tagType().c_str());
}
mediaTime = flvData.tagTime();
return true;

View file

@ -9,5 +9,5 @@ public:
private:
FLV::Tag flvData;
long long filter;
int64_t filter;
};

View file

@ -39,7 +39,7 @@ bool AnalyserH264::parsePacket(){
size = 0;
nalPtr = h264::nalFactory(dataBuffer.data(), dataBuffer.size(), size, !sizePrepended);
if (nalPtr){
HIGH_MSG("Read a %lu-byte NAL unit at position %llu", size, prePos);
HIGH_MSG("Read a %lu-byte NAL unit at position %" PRIu64, size, prePos);
if (detail >= 2){nalPtr->toPrettyString(std::cout);}
dataBuffer.erase(0, size); // erase the NAL unit we just read
prePos += size;
@ -47,7 +47,7 @@ bool AnalyserH264::parsePacket(){
///\TODO update mediaTime with current timestamp
}while (nalPtr);
if (!nalPtr){
FAIL_MSG("Could not read a NAL unit at position %llu", prePos);
FAIL_MSG("Could not read a NAL unit at position %" PRIu64, prePos);
return false;
}
return true;

View file

@ -40,7 +40,7 @@ void AnalyserHLS::getParts(const std::string &body){
}
if (!parsedPart || no > parsedPart){
HTTP::URL newURL = root.link(line);
INFO_MSG("Discovered #%llu: %s", no, newURL.getUrl().c_str());
INFO_MSG("Discovered #%" PRIu64 ": %s", no, newURL.getUrl().c_str());
parts.push_back(HLSPart(newURL, no, durat));
}
++no;
@ -111,7 +111,7 @@ bool AnalyserHLS::parsePacket(){
}
}
if (DL.data().size() % 188){
FAIL_MSG("Expected a multiple of 188 bytes, received %d bytes", DL.data().size());
FAIL_MSG("Expected a multiple of 188 bytes, received %zu bytes", DL.data().size());
return false;
}
parsedPart = part.no;
@ -124,8 +124,8 @@ bool AnalyserHLS::parsePacket(){
// Hm. I guess we had no parts to get.
if (refreshAt && refreshAt > Util::bootSecs()){
// We're getting a live stream. Let's wait and check again.
uint32_t sleepSecs = (refreshAt - Util::bootSecs());
INFO_MSG("Sleeping for %lu seconds", sleepSecs);
uint64_t sleepSecs = (refreshAt - Util::bootSecs());
INFO_MSG("Sleeping for %" PRIu64 " seconds", sleepSecs);
Util::sleep(sleepSecs * 1000);
}
// The non-live case is already handled in isOpen()

View file

@ -1,4 +1,5 @@
#include "analyser_mp4.h"
#include <mist/bitfields.h>
void AnalyserMP4::init(Util::Config &conf){
Analyser::init(conf);
@ -22,23 +23,21 @@ bool AnalyserMP4::parsePacket(){
}
if (mp4Data.read(mp4Buffer)){
INFO_MSG("Read a box at position %d", prePos);
INFO_MSG("Read a box at position %" PRIu64, prePos);
if (detail >= 2){std::cout << mp4Data.toPrettyString(0) << std::endl;}
///\TODO update mediaTime with the current timestamp
return true;
}
FAIL_MSG("Could not read box at position %llu", prePos);
FAIL_MSG("Could not read box at position %" PRIu64, prePos);
return false;
}
/// Calculates how many bytes we need to read a whole box.
uint64_t AnalyserMP4::neededBytes(){
if (mp4Buffer.size() < 4){return 4;}
uint64_t size = ntohl(((int *)mp4Buffer.data())[0]);
uint64_t size = Bit::btohl(mp4Buffer.data());
if (size != 1){return size;}
if (mp4Buffer.size() < 16){return 16;}
size = 0 + ntohl(((int *)mp4Buffer.data())[2]);
size <<= 32;
size += ntohl(((int *)mp4Buffer.data())[3]);
size = Bit::btohll(mp4Buffer.data() + 8);
return size;
}

View file

@ -28,18 +28,18 @@ bool AnalyserOGG::parsePacket(){
sn2Codec[oggPage.getBitstreamSerialNumber()] = "Opus";
}
if (sn2Codec[oggPage.getBitstreamSerialNumber()] != ""){
INFO_MSG("Bitstream %llu recognized as %s", oggPage.getBitstreamSerialNumber(),
INFO_MSG("Bitstream %" PRIu64 " recognized as %s", oggPage.getBitstreamSerialNumber(),
sn2Codec[oggPage.getBitstreamSerialNumber()].c_str());
}else{
WARN_MSG("Bitstream %llu not recognized!", oggPage.getBitstreamSerialNumber());
WARN_MSG("Bitstream %" PRIu64 " not recognized!", oggPage.getBitstreamSerialNumber());
}
}
if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Theora"){
if (detail >= 2){std::cout << " Theora data" << std::endl;}
static unsigned int numParts = 0;
static unsigned int keyCount = 0;
for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
static size_t numParts = 0;
static size_t keyCount = 0;
for (size_t i = 0; i < oggPage.getAllSegments().size(); i++){
theora::header tmpHeader((char *)oggPage.getSegment(i), oggPage.getAllSegments()[i].size());
if (tmpHeader.isHeader()){
if (tmpHeader.getHeaderType() == 0){kfgshift = tmpHeader.getKFGShift();}

View file

@ -8,8 +8,8 @@ public:
static void init(Util::Config &conf);
private:
std::map<int, std::string> sn2Codec;
std::map<uint64_t, std::string> sn2Codec;
std::string oggBuffer;
OGG::Page oggPage;
int kfgshift;
uint16_t kfgshift;
};

View file

@ -26,7 +26,7 @@ bool AnalyserRIFF::parsePacket(){
if (dataBuffer.size() < 8){return false;}
RIFF::Chunk C(dataBuffer.data(), dataBuffer.size());
INFO_MSG("Read a chunk at position %d", prePos);
INFO_MSG("Read a chunk at position %" PRIu64, prePos);
if (detail >= 2){C.toPrettyString(std::cout);}
///\TODO update mediaTime with the current timestamp
if (C){

View file

@ -2,6 +2,7 @@
/// Debugging tool for RTMP data.
#include "analyser_rtmp.h"
#include <mist/bitfields.h>
void AnalyserRTMP::init(Util::Config &conf){
Analyser::init(conf);
@ -42,8 +43,8 @@ bool AnalyserRTMP::parsePacket(){
// While we can't parse a packet,
while (!next.Parse(strbuf)){
// fill our internal buffer "strbuf" in (up to) 1024 byte chunks
if (std::cin.good()){
unsigned int charCount = 0;
if (!std::cin.good()){return false;}
size_t charCount = 0;
std::string tmpbuffer;
tmpbuffer.reserve(1024);
while (std::cin.good() && charCount < 1024){
@ -55,10 +56,6 @@ bool AnalyserRTMP::parsePacket(){
}
}
strbuf.append(tmpbuffer);
}else{
// if we can't fill the buffer, and have no parsable packet(s), return false
return false;
}
}
// We now know for sure that we've parsed a packet
@ -72,71 +69,66 @@ bool AnalyserRTMP::parsePacket(){
break; // happens when connection breaks unexpectedly
case 1: // set chunk size
RTMPStream::chunk_rec_max = ntohl(*(int *)next.data.c_str());
DETAIL_MED("CTRL: Set chunk size: %i", RTMPStream::chunk_rec_max);
DETAIL_MED("CTRL: Set chunk size: %" PRIu64, RTMPStream::chunk_rec_max);
break;
case 2: // abort message - we ignore this one
DETAIL_MED("CTRL: Abort message: %i", ntohl(*(int *)next.data.c_str()));
DETAIL_MED("CTRL: Abort message: %" PRIu32, Bit::btohl(next.data.data()));
// 4 bytes of stream id to drop
break;
case 3: // ack
RTMPStream::snd_window_at = ntohl(*(int *)next.data.c_str());
DETAIL_MED("CTRL: Acknowledgement: %i", RTMPStream::snd_window_at);
RTMPStream::snd_window_at = Bit::btohl(next.data.data());
DETAIL_MED("CTRL: Acknowledgement: %" PRIu64, RTMPStream::snd_window_at);
break;
case 4:{
short int ucmtype = ntohs(*(short int *)next.data.c_str());
int16_t ucmtype = Bit::btohs(next.data.data());
switch (ucmtype){
case 0:
DETAIL_MED("CTRL: User control message: stream begin %u",
ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_MED("CTRL: User control message: stream begin %" PRIu32, Bit::btohl(next.data.data() + 2));
break;
case 1:
DETAIL_MED("CTRL: User control message: stream EOF %u", ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_MED("CTRL: User control message: stream EOF %" PRIu32, Bit::btohl(next.data.data() + 2));
break;
case 2:
DETAIL_MED("CTRL: User control message: stream dry %u", ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_MED("CTRL: User control message: stream dry %" PRIu32, Bit::btohl(next.data.data() + 2));
break;
case 3:
DETAIL_MED("CTRL: User control message: setbufferlen %u",
ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_MED("CTRL: User control message: setbufferlen %" PRIu32, Bit::btohl(next.data.data() + 2));
break;
case 4:
DETAIL_MED("CTRL: User control message: streamisrecorded %u",
ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_MED("CTRL: User control message: streamisrecorded %" PRIu32, Bit::btohl(next.data.data() + 2));
break;
case 6:
DETAIL_MED("CTRL: User control message: pingrequest %u",
ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_MED("CTRL: User control message: pingrequest %" PRIu32, Bit::btohl(next.data.data() + 2));
break;
case 7:
DETAIL_MED("CTRL: User control message: pingresponse %u",
ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_MED("CTRL: User control message: pingresponse %" PRIu32, Bit::btohl(next.data.data() + 2));
break;
case 31:
case 32:
// don't know, but not interesting anyway
// don't know, but not interes ting anyway
break;
default:
DETAIL_LOW("CTRL: User control message: UNKNOWN %hu - %u", ucmtype,
ntohl(*(unsigned int *)(next.data.c_str() + 2)));
DETAIL_LOW("CTRL: User control message: UNKNOWN %" PRId16 " - %" PRIu32, ucmtype,
Bit::btohl(next.data.data() + 2));
break;
}
}break;
case 5: // window size of other end
RTMPStream::rec_window_size = ntohl(*(int *)next.data.c_str());
RTMPStream::rec_window_size = Bit::btohl(next.data.data());
RTMPStream::rec_window_at = RTMPStream::rec_cnt;
DETAIL_MED("CTRL: Window size: %i", RTMPStream::rec_window_size);
DETAIL_MED("CTRL: Window size: %" PRIu64, RTMPStream::rec_window_size);
break;
case 6:
RTMPStream::snd_window_size = ntohl(*(int *)next.data.c_str());
RTMPStream::snd_window_size = Bit::btohl(next.data.data());
// 4 bytes window size, 1 byte limit type (ignored)
DETAIL_MED("CTRL: Set peer bandwidth: %i", RTMPStream::snd_window_size);
DETAIL_MED("CTRL: Set peer bandwidth: %" PRIu64, RTMPStream::snd_window_size);
break;
case 8:
case 9:
if (detail >= 4 || reconstruct.good() || validate){
F.ChunkLoader(next);
mediaTime = F.tagTime();
DETAIL_VHI("[%llu+%llu] %s", F.tagTime(), F.offset(), F.tagType().c_str());
DETAIL_VHI("[%" PRIu64 "+%" PRId64 "] %s", F.tagTime(), F.offset(), F.tagType().c_str());
if (reconstruct.good()){reconstruct.write(F.data, F.len);}
}
break;

View file

@ -7,7 +7,7 @@ class AnalyserRTMP : public Analyser{
private:
RTMPStream::Chunk next; ///< Holds the most recently parsed RTMP chunk
FLV::Tag F; ///< Holds the most recently created FLV packet
unsigned int read_in; ///< Amounts of bytes read to fill 'strbuf' so far
size_t read_in; ///< Amounts of bytes read to fill 'strbuf' so far
Socket::Buffer strbuf; ///< Internal buffer from where 'next' is filled
AMF::Object amfdata; ///< Last read AMF object
AMF::Object3 amf3data; ///< Last read AMF3 object

View file

@ -14,9 +14,9 @@ void AnalyserRTSP::incoming(const DTSC::Packet &pkt){
char *dataPtr;
size_t dataSize;
pkt.getString("data", dataPtr, dataSize);
DETAIL_MED("Received %ub %sfor track %lu (%s) @ %llums", dataSize,
DETAIL_MED("Received %zub %sfor track %zu (%s) @ %" PRIu64 "ms", dataSize,
pkt.getFlag("keyframe") ? "keyframe " : "", pkt.getTrackId(),
myMeta.tracks[pkt.getTrackId()].getIdentifier().c_str(), pkt.getTime());
myMeta.getTrackIdentifier(pkt.getTrackId()).c_str(), pkt.getTime());
if (detail >= 8){
for (uint32_t i = 0; i < dataSize; ++i){
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)dataPtr[i] << " ";
@ -58,9 +58,9 @@ bool AnalyserRTSP::parsePacket(){
return true;
}
if (HTTP.hasHeader("Transport")){
uint32_t trackNo = sdpState.parseSetup(HTTP, "", "");
size_t trackNo = sdpState.parseSetup(HTTP, "", "");
if (trackNo){
DETAIL_MED("Parsed transport for track: %lu", trackNo);
DETAIL_MED("Parsed transport for track: %zu", trackNo);
}else{
DETAIL_MED("Could not parse transport string!");
}
@ -95,15 +95,15 @@ bool AnalyserRTSP::parsePacket(){
RTP::Packet pkt(tcpPacket.data() + 4, len);
uint8_t chan = tcpHead.data()[1];
uint32_t trackNo = sdpState.getTrackNoForChannel(chan);
DETAIL_HI("Received %ub RTP packet #%u on channel %u, time %llu", len,
(unsigned int)pkt.getSequence(), chan, pkt.getTimeStamp());
DETAIL_HI("Received %ub RTP packet #%u on channel %u, time %" PRIu32, len, pkt.getSequence(),
chan, pkt.getTimeStamp());
if (!trackNo && (chan % 2) != 1){
DETAIL_MED("Received packet for unknown track number on channel %u", chan);
}
if (trackNo){sdpState.tracks[trackNo].sorter.rtpSeq = pkt.getSequence();}
if (detail >= 10){
char *pl = pkt.getPayload();
const char *pl = pkt.getPayload();
uint32_t payLen = pkt.getPayloadSize();
for (uint32_t i = 0; i < payLen; ++i){
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)pl[i] << " ";

View file

@ -46,7 +46,7 @@ bool AnalyserTS::parsePacket(){
static char packetPtr[188];
std::cin.read(packetPtr, 188);
if (std::cin.gcount() != 188){return false;}
DONTEVEN_MSG("Reading from position %llu", bytes);
DONTEVEN_MSG("Reading from position %" PRIu64, bytes);
bytes += 188;
if (!packet.FromPointer(packetPtr)){return false;}
if (detail){
@ -74,15 +74,15 @@ bool AnalyserTS::parsePacket(){
}
AnalyserTS::~AnalyserTS(){
for (std::map<unsigned long long, std::string>::iterator it = payloads.begin(); it != payloads.end(); it++){
for (std::map<size_t, std::string>::iterator it = payloads.begin(); it != payloads.end(); it++){
if ((detail & 1) && (!pidOnly || it->first == pidOnly)){
std::cout << printPES(it->second, it->first);
}
}
}
std::string AnalyserTS::printPES(const std::string &d, unsigned long PID){
unsigned int headSize = 0;
std::string AnalyserTS::printPES(const std::string &d, size_t PID){
size_t headSize = 0;
std::stringstream res;
bool known = false;
res << "[PES " << PID << "]";
@ -98,7 +98,7 @@ std::string AnalyserTS::printPES(const std::string &d, unsigned long PID){
if (d[0] != 0 || d[1] != 0 || d[2] != 1){
res << " [!!! INVALID START CODE: " << (int)d[0] << " " << (int)d[1] << " " << (int)d[2] << " ]";
}
unsigned int padding = 0;
size_t padding = 0;
if (known){
if ((d[6] & 0xC0) != 0x80){res << " [!INVALID FIRST BITS!]";}
if (d[6] & 0x30){res << " [SCRAMBLED]";}
@ -144,19 +144,19 @@ std::string AnalyserTS::printPES(const std::string &d, unsigned long PID){
res << " [Padding: " << padding << "b]";
}
if (timeFlags & 0x02){
long long unsigned int time = (((unsigned int)d[9] & 0xE) >> 1);
uint64_t time = ((d[9] & 0xE) >> 1);
time <<= 15;
time |= ((unsigned int)d[10] << 7) | (((unsigned int)d[11] >> 1) & 0x7F);
time |= ((uint32_t)d[10] << 7) | ((d[11] >> 1) & 0x7F);
time <<= 15;
time |= ((unsigned int)d[12] << 7) | (((unsigned int)d[13] >> 1) & 0x7F);
time |= ((uint32_t)d[12] << 7) | ((d[13] >> 1) & 0x7F);
res << " [PTS " << ((double)time / 90000) << "s]";
}
if (timeFlags & 0x01){
long long unsigned int time = ((d[14] >> 1) & 0x07);
uint64_t time = ((d[14] >> 1) & 0x07);
time <<= 15;
time |= ((int)d[15] << 7) | (d[16] >> 1);
time |= ((uint32_t)d[15] << 7) | (d[16] >> 1);
time <<= 15;
time |= ((int)d[17] << 7) | (d[18] >> 1);
time |= ((uint32_t)d[17] << 7) | (d[18] >> 1);
res << " [DTS " << ((double)time / 90000) << "s]";
}
}
@ -169,12 +169,18 @@ std::string AnalyserTS::printPES(const std::string &d, unsigned long PID){
res << std::endl;
if (detail & 32){
unsigned int counter = 0;
for (unsigned int i = 9 + headSize + padding; i < d.size(); ++i){
size_t counter = 0;
for (size_t i = 9 + headSize + padding; i < d.size(); ++i){
if ((i < d.size() - 4) && d[i] == 0 && d[i + 1] == 0 && d[i + 2] == 0 && d[i + 3] == 1){
res << std::endl;
counter = 0;
}
if ((i < d.size() - 3) && d[i] == 0 && d[i + 1] == 0 && d[i + 2] == 1){
if (counter > 1){
res << std::endl << " ";
counter = 0;
}
}
res << std::hex << std::setw(2) << std::setfill('0') << (int)(d[i] & 0xff) << " ";
if ((counter) % 32 == 31){res << std::endl;}
counter++;

View file

@ -8,11 +8,11 @@ public:
~AnalyserTS();
bool parsePacket();
static void init(Util::Config &conf);
std::string printPES(const std::string &d, unsigned long PID);
std::string printPES(const std::string &d, size_t PID);
private:
std::map<unsigned long long, std::string> payloads;
uint32_t pidOnly;
std::map<size_t, std::string> payloads;
size_t pidOnly;
TS::Packet packet;
uint64_t bytes;
};

View file

@ -1,485 +0,0 @@
/// \file dash_analyzer.cpp
/// Contains the code for the DASH Analysing tool.
/// Currently, only mp4 is supported, and the xml parser assumes a representation id tag exists
#include <fstream>
#include <iostream>
#include <mist/config.h>
#include <mist/defines.h>
#include <mist/http_parser.h>
#include <mist/mp4.h>
#include <mist/mp4_generic.h>
#include <mist/timing.h>
#include <set>
#define OTHER 0x00
#define VIDEO 0x01
#define AUDIO 0x02
///\brief simple struct for storage of stream-specific data
struct StreamData{
long timeScale;
std::string media;
std::string initialization;
std::string initURL;
long trackID;
unsigned int adaptationSet;
unsigned char trackType;
};
StreamData tempSD; // temp global
///\brief another 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.timeScale) < (rhs.seekTime * timeScale)){
return true;
}else{
if ((seekTime * rhs.timeScale) == (rhs.seekTime * timeScale)){
if (adaptationSet < rhs.adaptationSet){
return true;
}else if (adaptationSet == rhs.adaptationSet){
if (trackID < rhs.trackID){return true;}
}
}
}
return false;
}
long timeScale;
long long unsigned int bytePos; /// ?
long long unsigned int seekTime; /// start
long long unsigned int duration; /// duration
unsigned int trackID; /// stores representation ID
unsigned int adaptationSet; /// stores type
unsigned char trackType; /// stores type
std::string url;
};
bool getDelimBlock(std::string &data, std::string name, size_t &blockStart, size_t &blockEnd, std::string delim){
size_t offset = data.find(name);
if (offset == std::string::npos){
return false; // name string not found.
}
// expected: delim character BEFORE blockstart.
offset--;
blockStart = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockStart: %i ", offset, blockStart);
offset = blockStart + 1; // skip single character!
blockEnd = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockEnd: %i ", offset, blockEnd);
if (blockStart == std::string::npos || blockEnd == std::string::npos){
return false; // no start/end quotes found
}
blockEnd++; // include delim
// DEBUG_MSG(DLVL_INFO, "getDelimPos: data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart) );
return true;
}
bool getValueBlock(std::string &data, std::string name, size_t &blockStart, size_t &blockEnd, std::string delim){
size_t offset = data.find(name);
if (offset == std::string::npos){
return false; // name string not found.
}
blockStart = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockStart: %i ", offset, blockStart);
blockStart++; // clip off quote characters
offset = blockStart; // skip single character!
blockEnd = data.find(delim, offset);
// DEBUG_MSG(DLVL_INFO, "offset: %i blockEnd: %i ", offset, blockEnd);
if (blockStart == std::string::npos || blockEnd == std::string::npos){
return false; // no start/end quotes found
}
// DEBUG_MSG(DLVL_INFO, "getValueBlock: data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart) );
return true;
}
bool getString(std::string &data, std::string name, std::string &output){
size_t blockStart = 0;
size_t blockEnd = 0;
if (!getValueBlock(data, name, blockStart, blockEnd, "\"")){
// DEBUG_MSG(DLVL_FAIL, "could not find \"%s\" in data block", name.c_str());
return false; // could not find value in this data block.
}
// DEBUG_MSG(DLVL_INFO, "data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart) )
output = data.substr(blockStart, (blockEnd - blockStart));
// looks like this function is working as expected
// DEBUG_MSG(DLVL_INFO, "data in getstring %s", (data.substr(blockStart,(blockEnd-blockStart))).c_str());
return true;
}
bool getLong(std::string &data, std::string name, long &output){
size_t blockStart, blockEnd;
if (!getValueBlock(data, name, blockStart, blockEnd, "\"")){
// DEBUG_MSG(DLVL_FAIL, "could not find \"%s\" in data block", name.c_str());
return false; // could not find value in this data block.
}
// DEBUG_MSG(DLVL_INFO, "name: %s data in atol %s",name.c_str(), (data.substr(blockStart,(blockEnd-blockStart))).c_str());
output = atol((data.substr(blockStart, (blockEnd - blockStart))).c_str());
return true;
}
// block expecting separate name and /name occurence, or name and /> before another occurence of <.
bool getBlock(std::string &data, std::string name, int offset, size_t &blockStart, size_t &blockEnd){
blockStart = data.find("<" + name + ">", offset);
if (blockStart == std::string::npos){
blockStart = data.find("<" + name + " ", offset); // this considers both valid situations <name> and <name bla="bla"/>
}
if (blockStart == std::string::npos){
DEBUG_MSG(DLVL_INFO, "no block start found for name: %s at offset: %i", name.c_str(), offset);
return false;
}
blockEnd = data.find("/" + name + ">", blockStart);
if (blockEnd == std::string::npos){
blockEnd = data.find("/>", blockStart);
if (blockEnd == std::string::npos){
DEBUG_MSG(DLVL_INFO, "no block end found.");
return false;
}
size_t temp = data.find("<", blockStart + 1, (blockEnd - blockStart - 1)); // the +1 is to avoid re-interpreting the starting < //TODO!!
if (temp != std::string::npos){// all info is epxected between <name ... />
DEBUG_MSG(DLVL_FAIL, "block start found before block end. offset: %lu block: %s", temp, data.c_str());
return false;
}
// DEBUG_MSG(DLVL_FAIL, "special block end found");
blockEnd += 2; // position after />
}else{
blockEnd += name.size() + 2; // position after /name>
}
// DEBUG_MSG(DLVL_INFO, "getBlock: start: %i end: %i",blockStart,blockEnd);
return true;
}
bool parseAdaptationSet(std::string &data, std::set<seekPos> &currentPos){
// DEBUG_MSG(DLVL_INFO, "Parsing adaptationSet: %s", data.c_str());
size_t offset = 0;
size_t blockStart, blockEnd;
tempSD.trackType = OTHER;
// get value: mimetype //todo: handle this!
std::string mimeType;
if (!getString(data, "mimeType", mimeType)){// get first occurence of mimeType. --> this will break
// if multiple mimetypes should be read from this block
// because no offset is provided. solution: use this on a substring containing the desired information.
DEBUG_MSG(DLVL_FAIL, "mimeType not found");
return false;
}
DEBUG_MSG(DLVL_INFO, "mimeType: %s", mimeType.c_str()); // checked, OK
if (mimeType.find("video") != std::string::npos){tempSD.trackType = VIDEO;}
if (mimeType.find("audio") != std::string::npos){tempSD.trackType = AUDIO;}
if (tempSD.trackType == OTHER){
DEBUG_MSG(DLVL_FAIL, "no audio or video type found. giving up.");
return false;
}
// find an ID within this adaptationSet block.
if (!getBlock(data, (std::string) "Representation", offset, blockStart, blockEnd)){
DEBUG_MSG(DLVL_FAIL, "Representation not found");
return false;
}
// representation string
std::string block = data.substr(blockStart, (blockEnd - blockStart));
DEBUG_MSG(DLVL_INFO, "Representation block: %s", block.c_str());
// check if block is not junk?
if (!getLong(block, "id", tempSD.trackID)){
DEBUG_MSG(DLVL_FAIL, "Representation id not found in block %s", block.c_str());
return false;
}
DEBUG_MSG(DLVL_INFO, "Representation/id: %li", tempSD.trackID); // checked, OK
offset = 0;
// get values from SegmentTemplate
if (!getBlock(data, (std::string) "SegmentTemplate", offset, blockStart, blockEnd)){
DEBUG_MSG(DLVL_FAIL, "SegmentTemplate not found");
return false;
}
block = data.substr(blockStart, (blockEnd - blockStart));
// DEBUG_MSG(DLVL_INFO, "SegmentTemplate block: %s",block.c_str()); //OK
getLong(block, "timescale", tempSD.timeScale);
getString(block, "media", tempSD.media);
getString(block, "initialization", tempSD.initialization);
size_t tmpBlockStart = 0;
size_t tmpBlockEnd = 0;
if (!getDelimBlock(tempSD.media, "RepresentationID", tmpBlockStart, tmpBlockEnd, "$")){
DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $RepresentationID$ in %s", tempSD.media.c_str());
return false;
}
tempSD.media.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
if (!getDelimBlock(tempSD.media, "Time", tmpBlockStart, tmpBlockEnd, "$")){
DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $Time$ in %s", tempSD.media.c_str());
return false;
}
tempSD.media.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
if (!getDelimBlock(tempSD.initialization, "RepresentationID", tmpBlockStart, tmpBlockEnd, "$")){
DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $RepresentationID$ in %s",
tempSD.initialization.c_str());
return false;
}
tempSD.initialization.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
// get segment timeline block from within segment template:
size_t blockOffset = 0; // offset should be 0 because this is a new block
if (!getBlock(block, "SegmentTimeline", blockOffset, blockStart, blockEnd)){
DEBUG_MSG(DLVL_FAIL, "SegmentTimeline block not found");
return false;
}
std::string block2 = block.substr(blockStart, (blockEnd - blockStart)); // overwrites previous block (takes just the segmentTimeline part
// DEBUG_MSG(DLVL_INFO, "SegmentTimeline block: %s",block2.c_str()); //OK
int numS = 0;
offset = 0;
long long unsigned int totalDuration = 0;
long timeValue;
while (1){
if (!getBlock(block2, "S", offset, blockStart, blockEnd)){
if (numS == 0){
DEBUG_MSG(DLVL_FAIL, "no S found within SegmentTimeline");
return false;
}else{
DEBUG_MSG(DLVL_INFO, "all S found within SegmentTimeline %i", numS);
return true; // break; //escape from while loop (to return true)
}
}
numS++;
// stuff S data into: currentPos
// searching for t(start position)
std::string sBlock = block2.substr(blockStart, (blockEnd - blockStart));
// DEBUG_MSG(DLVL_INFO, "S found. offset: %i blockStart: %i blockend: %i block: %s",offset,blockStart, blockEnd, sBlock.c_str()); //OK!
if (getLong(sBlock, "t", timeValue)){
totalDuration = timeValue; // reset totalDuration to value of t
}
if (!getLong(sBlock, "d", timeValue)){// expected duration in every S.
DEBUG_MSG(DLVL_FAIL, "no d found within S");
return false;
}
// stuff data with old value (start of block)
// DEBUG_MSG(DLVL_INFO, "stuffing info from S into set");
seekPos thisPos;
thisPos.trackType = tempSD.trackType;
thisPos.trackID = tempSD.trackID;
thisPos.adaptationSet = tempSD.adaptationSet;
// thisPos.trackID=id;
thisPos.seekTime = totalDuration; // previous total duration is start time of this S.
thisPos.duration = timeValue;
thisPos.timeScale = tempSD.timeScale;
static char charBuf[512];
snprintf(charBuf, 512, tempSD.media.c_str(), tempSD.trackID, totalDuration);
thisPos.url.assign(charBuf);
// DEBUG_MSG(DLVL_INFO, "media url (from rep.ID %d, startTime %d): %s", tempSD.trackID, totalDuration,thisPos.url.c_str());
currentPos.insert(thisPos); // assumes insert copies all data in seekPos struct.
totalDuration += timeValue; // update totalDuration
offset = blockEnd; // blockEnd and blockStart are absolute values within string, offset is not relevant.
}
return true;
}
bool parseXML(std::string &body, std::set<seekPos> &currentPos, std::vector<StreamData> &streamData){
// for all adaptation sets
// representation ID
int numAdaptationSet = 0;
size_t currentOffset = 0;
size_t adaptationSetStart;
size_t adaptationSetEnd;
// DEBUG_MSG(DLVL_INFO, "body received: %s", body.c_str());
while (getBlock(body, "AdaptationSet", currentOffset, adaptationSetStart, adaptationSetEnd)){
tempSD.adaptationSet = numAdaptationSet;
numAdaptationSet++;
DEBUG_MSG(DLVL_INFO, "adaptationSet found. start: %lu end: %lu num: %lu ", adaptationSetStart,
adaptationSetEnd, (adaptationSetEnd - adaptationSetStart));
// get substring: from <adaptationSet... to /adaptationSet>
std::string adaptationSet = body.substr(adaptationSetStart, (adaptationSetEnd - adaptationSetStart));
// function was verified: output as expected.
if (!parseAdaptationSet(adaptationSet, currentPos)){
DEBUG_MSG(DLVL_FAIL, "parseAdaptationSet returned false."); // this also happens in the case
// of OTHER mimetype. in that case it might be desirable to continue searching for valid data instead of quitting.
return false;
}
streamData.push_back(tempSD); // put temp values into adaptation set vector
currentOffset = adaptationSetEnd; // the getblock function should make sure End is at the correct offset.
}
if (numAdaptationSet == 0){
DEBUG_MSG(DLVL_FAIL, "no adaptationSet found.");
return false;
}
DEBUG_MSG(DLVL_INFO, "all adaptation sets found. total: %i", numAdaptationSet);
return true;
}
int main(int argc, char **argv){
Util::Config conf = Util::Config(argv[0]);
conf.addOption("mode",
JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", "
"\"default\":\"analyse\", \"help\":\"What to do with the stream. "
"Valid modes are 'analyse', 'validate', 'output'.\"}"));
conf.addOption("url", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"URL to "
"HLS stream index file to retrieve.\"}"));
conf.addOption("abort", JSON::fromString("{\"long\":\"abort\", \"short\":\"a\", "
"\"arg\":\"integer\", \"default\":-1, \"help\":\"Abort "
"after this many seconds of downloading. Negative "
"values mean unlimited, which is the default.\"}"));
conf.parseArgs(argc, argv);
conf.activate();
unsigned int port = 80;
std::string url = conf.getString("url");
if (url.substr(0, 7) != "http://"){
DEBUG_MSG(DLVL_FAIL, "The URL must start with http://");
return -1;
}
url = url.substr(7); // found problem if url is to short!! it gives out of range when entering http://meh.meh
std::string server = url.substr(0, url.find('/'));
url = url.substr(url.find('/'));
if (server.find(':') != std::string::npos){
port = atoi(server.substr(server.find(':') + 1).c_str());
server = server.substr(0, server.find(':'));
}
long long int startTime = Util::bootSecs();
long long int abortTime = conf.getInteger("abort");
Socket::Connection conn(server, port, false);
// url:
DEBUG_MSG(DLVL_INFO, "url %s server: %s port: %d", url.c_str(), server.c_str(), port);
std::string urlPrependStuff = url.substr(0, url.rfind("/") + 1);
DEBUG_MSG(DLVL_INFO, "prepend stuff: %s", urlPrependStuff.c_str());
if (!conn){conn.open(server, port, false);}
unsigned int pos = 0;
HTTP::Parser H;
H.url = url;
H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString());
H.SendRequest(conn);
H.Clean();
while (conn && (!conn.spool() || !H.Read(conn))){}
H.BuildResponse();
std::set<seekPos> currentPos;
std::vector<StreamData> streamData;
// DEBUG_MSG(DLVL_INFO, "body received: %s", H.body.c_str()); //keeps giving empty stuff :(
// DEBUG_MSG(DLVL_INFO, "url %s ", url.c_str());
// std::ifstream in(url.c_str());
// std::string s((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
if (!parseXML(H.body, currentPos, streamData)){
DEBUG_MSG(DLVL_FAIL, "Manifest parsing failed. body: \n %s", H.body.c_str());
if (conf.getString("mode") == "validate"){
long long int endTime = Util::bootSecs();
std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
}
return -1;
}
H.Clean();
DEBUG_MSG(DLVL_INFO, "*********");
DEBUG_MSG(DLVL_INFO, "*SUMMARY*");
DEBUG_MSG(DLVL_INFO, "*********");
DEBUG_MSG(DLVL_INFO, "num streams: %lu", streamData.size());
for (unsigned int i = 0; i < streamData.size(); i++){
DEBUG_MSG(DLVL_INFO, "");
DEBUG_MSG(DLVL_INFO, "ID in vector %d", i);
DEBUG_MSG(DLVL_INFO, "trackID %ld", streamData[i].trackID);
DEBUG_MSG(DLVL_INFO, "adaptationSet %d", streamData[i].adaptationSet);
DEBUG_MSG(DLVL_INFO, "trackType (audio 0x02, video 0x01) %d", streamData[i].trackType);
DEBUG_MSG(DLVL_INFO, "TimeScale %ld", streamData[i].timeScale);
DEBUG_MSG(DLVL_INFO, "Media string %s", streamData[i].media.c_str());
DEBUG_MSG(DLVL_INFO, "Init string %s", streamData[i].initialization.c_str());
}
DEBUG_MSG(DLVL_INFO, "");
for (unsigned int i = 0; i < streamData.size(); i++){// get init url
static char charBuf[512];
snprintf(charBuf, 512, streamData[i].initialization.c_str(), streamData[i].trackID);
streamData[i].initURL.assign(charBuf);
DEBUG_MSG(DLVL_INFO, "init url for adaptationSet %d trackID %ld: %s ",
streamData[i].adaptationSet, streamData[i].trackID, streamData[i].initURL.c_str());
}
while (currentPos.size() && (abortTime <= 0 || Util::bootSecs() < startTime + abortTime)){
// DEBUG_MSG(DLVL_INFO, "next url: %s", currentPos.begin()->url.c_str());
// match adaptation set and track id?
int tempID = 0;
for (unsigned int i = 0; i < streamData.size(); i++){
if (streamData[i].trackID == currentPos.begin()->trackID &&
streamData[i].adaptationSet == currentPos.begin()->adaptationSet)
tempID = i;
}
if (!conn){conn.open(server, port, false);}
HTTP::Parser H;
H.url = urlPrependStuff;
H.url.append(currentPos.begin()->url);
DEBUG_MSG(DLVL_INFO, "Retrieving segment: %s (%llu-%llu)", H.url.c_str(),
currentPos.begin()->seekTime, currentPos.begin()->seekTime + currentPos.begin()->duration);
H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString()); // wut?
H.SendRequest(conn);
// TODO: get response?
H.Clean();
while (conn && (!conn.spool() || !H.Read(conn))){}// ehm...
// std::cout << "leh vomi: "<<H.body <<std::endl;
// DEBUG_MSG(DLVL_INFO, "zut: %s", H.body.c_str());
// strBuf[tempID].append(H.body);
if (!H.body.size()){
DEBUG_MSG(DLVL_FAIL, "No data downloaded from %s", H.url.c_str());
break;
}
size_t beforeParse = H.body.size();
MP4::Box mp4Data;
bool mdatSeen = false;
while (mp4Data.read(H.body)){
if (mp4Data.isType("mdat")){mdatSeen = true;}
}
if (!mdatSeen){
DEBUG_MSG(DLVL_FAIL, "No mdat present. Sadface. :-(");
break;
}
if (H.body.size()){
DEBUG_MSG(DLVL_FAIL, "%lu bytes left in body. Assuming horrible things...", H.body.size()); //,H.body.c_str());
std::cerr << H.body << std::endl;
if (beforeParse == H.body.size()){break;}
}
H.Clean();
pos = 1000 * (currentPos.begin()->seekTime + currentPos.begin()->duration) / streamData[tempID].timeScale;
if (conf.getString("mode") == "validate" && (Util::bootSecs() - startTime + 5) * 1000 < pos){
Util::wait(pos - (Util::bootSecs() - startTime + 5) * 1000);
}
currentPos.erase(currentPos.begin());
}
if (conf.getString("mode") == "validate"){
long long int endTime = Util::bootSecs();
std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
}
return 0;
}

View file

@ -0,0 +1,78 @@
#include <cstdio>
#include <iostream>
#include <string>
#include <mist/bitfields.h>
#include <mist/config.h>
#include <mist/defines.h>
#include <mist/h264.h>
///\brief Holds everything unique to the analysers.
namespace Analysers{
int analyseH264(Util::Config conf){
FILE *F = fopen(conf.getString("filename").c_str(), "r+b");
if (!F){FAIL_MSG("No such file");}
h264::nalUnit *nalPtr = h264::nalFactory(F);
while (nalPtr){
if (nalPtr->getType() == 0x07){
Utils::bitstream br;
br << nalPtr->payload;
Utils::bitWriter bw;
bw.append(br.get(8), 8); // nalType
bw.append(br.get(8), 8); // profile_idc
bw.append(br.get(8), 8); // constraint flags
bw.append(br.get(8), 8); // level_idc
br.getUExpGolomb();
bw.appendUExpGolomb(0); // seq_parameter_set_id
while (br.size() >= 64){bw.append(br.get(64), 64);}
size_t remainder = br.size();
if (remainder){bw.append(br.get(remainder), remainder);}
nalPtr->payload = bw.str();
}else if (nalPtr->getType() == 0x08){
Utils::bitstream br;
br << nalPtr->payload;
Utils::bitWriter bw;
bw.append(br.get(8), 8); // nalType
br.getUExpGolomb();
bw.appendUExpGolomb(0); // pic_parameter_set_id
br.getUExpGolomb();
bw.appendUExpGolomb(0); // seq_parameter_set_id
while (br.size() >= 64){bw.append(br.get(64), 64);}
size_t remainder = br.size();
if (remainder){bw.append(br.get(remainder), remainder);}
nalPtr->payload = bw.str();
}else if (nalPtr->getType() == 0x01 || nalPtr->getType() == 0x05 || nalPtr->getType() == 0x19){
Utils::bitstream br;
br << nalPtr->payload;
Utils::bitWriter bw;
bw.append(br.get(8), 8); // nalType
bw.appendUExpGolomb(br.getUExpGolomb()); // first_mb_in_slice
bw.appendUExpGolomb(br.getUExpGolomb()); // slice_type
br.getUExpGolomb();
bw.appendUExpGolomb(0); // pic_parameter_set_id
while (br.size() >= 64){bw.append(br.get(64), 64);}
size_t remainder = br.size();
if (remainder){bw.append(br.get(remainder), remainder);}
nalPtr->payload = bw.str();
}
nalPtr->write(std::cout);
delete nalPtr;
nalPtr = h264::nalFactory(F);
}
return 0;
}
}// namespace Analysers
int main(int argc, char **argv){
Util::Config conf = Util::Config(argv[0]);
conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Full "
"path of the file to analyse.\"}"));
conf.parseArgs(argc, argv);
return Analysers::analyseH264(conf);
}

View file

@ -476,6 +476,31 @@ int main_loop(int argc, char **argv){
}
}
// Upgrade old configurations
{
bool foundCMAF = false;
bool edit = false;
JSON::Value newVal;
jsonForEach(Controller::Storage["config"]["protocols"], it){
if ((*it)["connector"].asStringRef() == "HSS"){
edit = true;
continue;
}
if ((*it)["connector"].asStringRef() == "DASH"){
edit = true;
continue;
}
if ((*it)["connector"].asStringRef() == "CMAF"){foundCMAF = true;}
newVal.append(*it);
}
if (edit && !foundCMAF){newVal.append(JSON::fromString("{\"connector\":\"CMAF\"}"));}
if (edit){
Controller::Storage["config"]["protocols"] = newVal;
Controller::Log("CONF", "Translated protocols to new versions");
}
}
Controller::Log("CONF", "Controller started");
// Generate instanceId once per boot.
if (Controller::instanceId == ""){

View file

@ -95,17 +95,11 @@ namespace Controller{
trgs["STREAM_PUSH"]["response"] = "always";
trgs["STREAM_PUSH"]["response_action"] = "If false, rejects the incoming push.";
trgs["STREAM_TRACK_ADD"]["when"] = "Before a new track is accepted by a live stream buffer";
trgs["STREAM_TRACK_ADD"]["stream_specific"] = true;
trgs["STREAM_TRACK_ADD"]["payload"] = "stream name (string)\ntrack ID (integer)\n";
trgs["STREAM_TRACK_ADD"]["response"] = "ignored";
trgs["STREAM_TRACK_ADD"]["response_action"] = "None.";
trgs["STREAM_TRACK_REMOVE"]["when"] = "Before a track is removed by a live stream buffer";
trgs["STREAM_TRACK_REMOVE"]["stream_specific"] = true;
trgs["STREAM_TRACK_REMOVE"]["payload"] = "stream name (string)\ntrack ID (integer)\n";
trgs["STREAM_TRACK_REMOVE"]["response"] = "ignored";
trgs["STREAM_TRACK_REMOVE"]["response_action"] = "None.";
trgs["LIVE_TRACK_LIST"]["when"] = "After the list of valid tracks has been updated";
trgs["LIVE_TRACK_LIST"]["stream_specific"] = true;
trgs["LIVE_TRACK_LIST"]["payload"] = "stream name (string)\ntrack list (JSON)\n";
trgs["LIVE_TRACK_LIST"]["response"] = "ignored";
trgs["LIVE_TRACK_LIST"]["response_action"] = "None.";
trgs["STREAM_BUFFER"]["when"] = "Every time a live stream buffer changes state";
trgs["STREAM_BUFFER"]["stream_specific"] = true;

View file

@ -145,17 +145,18 @@ namespace Controller{
if (pipedCapa.isMember("optional")){builPipedPart(p, argarr, argnum, pipedCapa["optional"]);}
}
///\brief Checks current protocol configuration, updates state of enabled connectors if neccessary.
///\param p An object containing all protocols.
///\param capabilities An object containing the detected capabilities.
///\returns True if any action was taken
///\brief Checks current protocol configuration, updates state of enabled connectors if
/// neccessary. \param p An object containing all protocols. \param capabilities An object
/// containing the detected capabilities. \returns True if any action was taken
///
/// \triggers
/// The `"OUTPUT_START"` trigger is global, and is ran whenever a new protocol listener is started. It cannot be cancelled. Its payload is:
/// The `"OUTPUT_START"` trigger is global, and is ran whenever a new protocol listener is
/// started. It cannot be cancelled. Its payload is:
/// ~~~~~~~~~~~~~~~
/// output listener commandline
/// ~~~~~~~~~~~~~~~
/// The `"OUTPUT_STOP"` trigger is global, and is ran whenever a protocol listener is terminated. It cannot be cancelled. Its payload is:
/// The `"OUTPUT_STOP"` trigger is global, and is ran whenever a protocol listener is terminated.
/// It cannot be cancelled. Its payload is:
/// ~~~~~~~~~~~~~~~
/// output listener commandline
/// ~~~~~~~~~~~~~~~

View file

@ -118,11 +118,12 @@ namespace Controller{
for (unsigned int i = 0; i < 8; ++i){
aesKey[15 - i] = ((currID >> (i * 8)) + aesKey[15 - i]) & 0xFF;
}
char ivec[16];
memset(ivec, 0, 16);
Encryption::AES crypter;
crypter.setEncryptKey(aesKey);
// 0 here for 0-filled ivec.
dl.setHeader("X-IRDGAF",
Encodings::Base64::encode(Encryption::AES_Crypt(
RELEASE "|" PACKAGE_VERSION, sizeof(RELEASE "|" PACKAGE_VERSION), aesKey, ivec)));
Encodings::Base64::encode(crypter.encryptBlockCTR(0, RELEASE "|" PACKAGE_VERSION)));
}
if (!dl.get(url) || !dl.isOk()){return;}
response = JSON::fromString(dl.data());
@ -143,11 +144,12 @@ namespace Controller{
aesKey[15 - i] = ((licID >> (i * 8)) + aesKey[15 - i]) & 0xFF;
}
std::string cipher = Encodings::Base64::decode(input);
std::string deCrypted;
// magic ivecs, they are empty. It's secretly 16 times \0.
char ivec[16];
memset(ivec, 0, 16);
deCrypted = Encryption::AES_Crypt(cipher.c_str(), cipher.size(), aesKey, ivec);
Encryption::AES crypter;
crypter.setEncryptKey(aesKey);
// 0 here for 0-filled ivec.
std::string deCrypted = crypter.encryptBlockCTR(0, cipher);
// get time stamps and license.
// verify checksum

View file

@ -135,7 +135,10 @@ namespace Controller{
void pushCheckLoop(void *np){
{
IPC::sharedPage pushReadPage("MstPush", 8 * 1024 * 1024, false, false);
if (pushReadPage.mapped){readPushList(pushReadPage.mapped);}
if (pushReadPage.mapped){
readPushList(pushReadPage.mapped);
pushReadPage.master = true;
}
}
pushListRead = true;
IPC::sharedPage pushPage("MstPush", 8 * 1024 * 1024, true, false);

View file

@ -30,6 +30,7 @@
#define STAT_CLI_BPS_DOWN 128
#define STAT_CLI_BPS_UP 256
#define STAT_CLI_CRC 512
#define STAT_CLI_SESSID 1024
#define STAT_CLI_ALL 0xFFFF
// These are used to store "totals" field requests in a bitfield for speedup.
#define STAT_TOT_CLIENTS 1
@ -48,6 +49,8 @@ std::map<std::string, Controller::triggerLog> Controller::triggerStats; ///< Hol
bool Controller::killOnExit = KILL_ON_EXIT;
tthread::mutex Controller::statsMutex;
unsigned int Controller::maxConnsPerIP = 0;
uint64_t Controller::statDropoff = 0;
char noBWCountMatches[1717];
uint64_t bwLimit = 128 * 1024 * 1024; // gigabit default limit
@ -96,35 +99,27 @@ static uint64_t servInputs = 0;
static uint64_t servOutputs = 0;
static uint64_t servViewers = 0;
Controller::sessIndex::sessIndex(std::string dhost, unsigned int dcrc, std::string dstreamName,
std::string dconnector){
ID = "UNSET";
host = dhost;
crc = dcrc;
streamName = dstreamName;
connector = dconnector;
}
Controller::sessIndex::sessIndex(){
crc = 0;
}
/// Initializes a sessIndex from a statistics object + index, converting binary format IP addresses
/// into strings. This extracts the host, stream name, connector and crc field, ignoring everything
/// else.
Controller::sessIndex::sessIndex(const Comms::Statistics &statComm, size_t id){
host = statComm.getHost(id);
streamName = statComm.getStream(id);
connector = statComm.getConnector(id);
crc = statComm.getCRC(id);
ID = statComm.getSessId(id);
}
std::string Controller::sessIndex::toStr(){
std::stringstream s;
s << ID << "(" << host << " " << crc << " " << streamName << " " << connector << ")";
return s.str();
}
/// Initializes a sessIndex from a statExchange object, converting binary format IP addresses into
/// strings. This extracts the host, stream name, connector and crc field, ignoring everything else.
Controller::sessIndex::sessIndex(IPC::statExchange &data){
Socket::hostBytesToStr(data.host().c_str(), 16, host);
streamName = data.streamName();
connector = data.connector();
crc = data.crc();
ID = data.getSessId();
}
bool Controller::sessIndex::operator==(const Controller::sessIndex &b) const{
return (host == b.host && crc == b.crc && streamName == b.streamName && connector == b.connector);
}
@ -166,13 +161,13 @@ void Controller::streamStopped(std::string stream){
INFO_MSG("Stream %s became inactive", stream.c_str());
}
/// \todo Make this prettier.
IPC::sharedServer *statPointer = 0;
Comms::Statistics statComm;
bool statCommActive = false;
/// Invalidates all current sessions for the given streamname
/// Updates the session cache, afterwards.
void Controller::sessions_invalidate(const std::string &streamname){
if (!statPointer){
if (!statCommActive){
FAIL_MSG("In shutdown procedure - cannot invalidate sessions.");
return;
}
@ -209,7 +204,7 @@ void Controller::sessions_shutdown(JSON::Iter &i){
/// Shuts down the given session
/// Updates the session cache, afterwards.
void Controller::sessId_shutdown(const std::string &sessId){
if (!statPointer){
if (!statCommActive){
FAIL_MSG("In controller shutdown procedure - cannot shutdown sessions.");
return;
}
@ -231,7 +226,7 @@ void Controller::sessId_shutdown(const std::string &sessId){
/// Tags the given session
void Controller::sessId_tag(const std::string &sessId, const std::string &tag){
if (!statPointer){
if (!statCommActive){
FAIL_MSG("In controller shutdown procedure - cannot tag sessions.");
return;
}
@ -250,7 +245,7 @@ void Controller::sessId_tag(const std::string &sessId, const std::string &tag){
/// Shuts down sessions with the given tag set
/// Updates the session cache, afterwards.
void Controller::tag_shutdown(const std::string &tag){
if (!statPointer){
if (!statCommActive){
FAIL_MSG("In controller shutdown procedure - cannot shutdown sessions.");
return;
}
@ -272,7 +267,7 @@ void Controller::tag_shutdown(const std::string &tag){
/// Shuts down all current sessions for the given streamname
/// Updates the session cache, afterwards.
void Controller::sessions_shutdown(const std::string &streamname, const std::string &protocol){
if (!statPointer){
if (!statCommActive){
FAIL_MSG("In controller shutdown procedure - cannot shutdown sessions.");
return;
}
@ -325,9 +320,13 @@ void Controller::writeSessionCache(){
/// old statistics that have disconnected over 10 minutes ago.
void Controller::SharedMemStats(void *config){
HIGH_MSG("Starting stats thread");
IPC::sharedServer statServer(SHM_STATISTICS, STAT_EX_SIZE, true);
statPointer = &statServer;
statComm.reload(true);
statCommActive = true;
shmSessions = new IPC::sharedPage(SHM_SESSIONS, SHM_SESSIONS_SIZE, false, false);
if (!shmSessions || !shmSessions->mapped){
if (shmSessions){delete shmSessions;}
shmSessions = new IPC::sharedPage(SHM_SESSIONS, SHM_SESSIONS_SIZE, true);
}
cacheLock = new IPC::semaphore(SEM_SESSCACHE, O_CREAT | O_RDWR, ACCESSPERMS, 1);
cacheLock->unlink();
cacheLock->open(SEM_SESSCACHE, O_CREAT | O_RDWR, ACCESSPERMS, 1);
@ -341,7 +340,10 @@ void Controller::SharedMemStats(void *config){
tthread::lock_guard<tthread::mutex> guard2(statsMutex);
cacheLock->wait(); /*LTS*/
// parse current users
statServer.parseEach(parseStatistics);
statLeadIn();
COMM_LOOP(statComm, statOnActive(id), statOnDisconnect(id));
statLeadOut();
if (firstRun){
firstRun = false;
servUpOtherBytes = 0;
@ -357,18 +359,27 @@ void Controller::SharedMemStats(void *config){
// wipe old statistics
if (sessions.size()){
std::list<sessIndex> mustWipe;
unsigned long long cutOffPoint = Util::epoch() - STAT_CUTOFF;
unsigned long long disconnectPointIn = Util::epoch() - STATS_INPUT_DELAY;
unsigned long long disconnectPointOut = Util::epoch() - STATS_DELAY;
uint64_t cutOffPoint = Util::bootSecs() - STAT_CUTOFF;
uint64_t disconnectPointIn = Util::bootSecs() - STATS_INPUT_DELAY;
uint64_t disconnectPointOut = Util::bootSecs() - STATS_DELAY;
for (std::map<sessIndex, statSession>::iterator it = sessions.begin(); it != sessions.end(); it++){
unsigned long long dPoint = it->second.getSessType() == SESS_INPUT ? disconnectPointIn : disconnectPointOut;
it->second.ping(it->first, dPoint);
uint64_t dPoint = it->second.getSessType() == SESS_INPUT ? disconnectPointIn : disconnectPointOut;
if (it->second.sync == 100){
// Denied entries are connection-entry-wiped as soon as they become boring
it->second.wipeOld(dPoint);
}else{
// Normal entries are summarized after STAT_CUTOFF seconds
it->second.wipeOld(cutOffPoint);
}
if (!it->second.hasData()){mustWipe.push_back(it->first);}
// This part handles ending sessions, keeping them in cache for now
if (it->second.isTracked() && !it->second.isConnected() && it->second.getEnd() < dPoint){
it->second.dropSession(it->first);
}
// This part handles wiping from the session cache
if (!it->second.hasData()){
it->second.dropSession(it->first); // End the session, just in case it wasn't yet
mustWipe.push_back(it->first);
}
}
while (mustWipe.size()){
sessions.erase(mustWipe.front());
@ -429,15 +440,18 @@ void Controller::SharedMemStats(void *config){
}
Util::wait(1000);
}
statPointer = 0;
statCommActive = false;
HIGH_MSG("Stopping stats thread");
if (Util::Config::is_restarting){
statServer.abandon();
statComm.setMaster(false);
shmSessions->master = false;
}else{/*LTS-START*/
if (Controller::killOnExit){
WARN_MSG("Killing all connected clients to force full shutdown");
statServer.finishEach();
for (uint32_t id = statComm.firstValid(); id != statComm.endValid(); id++){
if (statComm.getStatus(id) == COMM_STATUS_INVALID){continue;}
statComm.kill(id, true);
}
}
/*LTS-END*/
}
@ -474,12 +488,10 @@ std::set<std::string> Controller::getActiveStreams(const std::string &prefix){
uint32_t Controller::statSession::invalidate(){
uint32_t ret = 0;
sync = 1;
if (curConns.size() && statPointer){
if (curConns.size() && statCommActive){
for (std::map<uint64_t, statStorage>::iterator jt = curConns.begin(); jt != curConns.end(); ++jt){
char *data = statPointer->getIndex(jt->first);
if (data){
IPC::statExchange tmpEx(data);
tmpEx.setSync(2);
if (statComm.getStatus(jt->first) != COMM_STATUS_INVALID){
statComm.setSync(2, jt->first);
ret++;
}
}
@ -492,16 +504,14 @@ uint32_t Controller::statSession::invalidate(){
uint32_t Controller::statSession::kill(){
uint32_t ret = 0;
sync = 100;
if (curConns.size() && statPointer){
if (curConns.size() && statCommActive){
for (std::map<uint64_t, statStorage>::iterator jt = curConns.begin(); jt != curConns.end(); ++jt){
char *data = statPointer->getIndex(jt->first);
if (data){
IPC::statExchange tmpEx(data);
tmpEx.setSync(100);
uint32_t pid = tmpEx.getPID();
if (statComm.getStatus(jt->first) != COMM_STATUS_INVALID){
statComm.setSync(100, jt->first);
uint32_t pid = statComm.getPid(jt->first);
if (pid > 1){
Util::Procs::Stop(pid);
INFO_MSG("Killing PID %lu", pid);
INFO_MSG("Killing PID %" PRIu32, pid);
}
ret++;
}
@ -511,15 +521,18 @@ uint32_t Controller::statSession::kill(){
}
/// Updates the given active connection with new stats data.
void Controller::statSession::update(uint64_t index, IPC::statExchange &data){
// update the sync byte: 0 = requesting fill, 2 = requesting refill, 1 = needs checking, > 2 = state known (100=denied, 10=accepted)
if (!data.getSync()){
sessIndex tmpidx(data);
std::string myHost = tmpidx.host;
void Controller::statSession::update(uint64_t index, Comms::Statistics &statComm){
std::string myHost = statComm.getHost(index);
std::string myStream = statComm.getStream(index);
std::string myConnector = statComm.getConnector(index);
// update the sync byte: 0 = requesting fill, 2 = requesting refill, 1 = needs checking, > 2 =
// state known (100=denied, 10=accepted)
if (!statComm.getSync(index)){
sessIndex tmpidx(statComm, index);
// if we have a maximum connection count per IP, enforce it
if (maxConnsPerIP && !data.getSync()){
if (maxConnsPerIP && !statComm.getSync(index)){
unsigned int currConns = 1;
long long shortly = Util::epoch();
long long shortly = Util::bootSecs();
for (std::map<sessIndex, statSession>::iterator it = sessions.begin(); it != sessions.end(); it++){
if (&it->second != this && it->first.host == myHost &&
@ -533,35 +546,35 @@ void Controller::statSession::update(uint64_t index, IPC::statExchange &data){
}
if (currConns > maxConnsPerIP){
WARN_MSG("Disconnecting session from %s: exceeds max connection count of %u", myHost.c_str(), maxConnsPerIP);
data.setSync(100);
statComm.setSync(100, index);
}
}
if (data.getSync() != 100){
if (statComm.getSync(index) != 100){
// only set the sync if this is the first connection in the list
// we also catch the case that there are no connections, which is an error-state
if (!sessions[tmpidx].curConns.size() || sessions[tmpidx].curConns.begin()->first == index){
MEDIUM_MSG("Requesting sync to %u for %s, %s, %s, %lu", sync, data.streamName().c_str(),
data.connector().c_str(), myHost.c_str(), data.crc() & 0xFFFFFFFFu);
data.setSync(sync);
MEDIUM_MSG("Requesting sync to %u for %s, %s, %s, %" PRIu32, sync, myStream.c_str(),
myConnector.c_str(), myHost.c_str(), statComm.getCRC(index) & 0xFFFFFFFFu);
statComm.setSync(sync, index);
}
// and, always set the sync if it is > 2
if (sync > 2){
MEDIUM_MSG("Setting sync to %u for %s, %s, %s, %lu", sync, data.streamName().c_str(),
data.connector().c_str(), myHost.c_str(), data.crc() & 0xFFFFFFFFu);
data.setSync(sync);
MEDIUM_MSG("Setting sync to %u for %s, %s, %s, %" PRIu32, sync, myStream.c_str(),
myConnector.c_str(), myHost.c_str(), statComm.getCRC(index) & 0xFFFFFFFFu);
statComm.setSync(sync, index);
}
}
}else{
if (sync < 2 && data.getSync() > 2){sync = data.getSync();}
if (sync < 2 && statComm.getSync(index) > 2){sync = statComm.getSync(index);}
}
long long prevDown = getDown();
long long prevUp = getUp();
curConns[index].update(data);
curConns[index].update(statComm, index);
// store timestamp of first received data, if older
if (firstSec > data.now()){firstSec = data.now();}
if (firstSec > statComm.getNow(index)){firstSec = statComm.getNow(index);}
// store timestamp of last received data, if newer
if (data.now() > lastSec){
lastSec = data.now();
if (statComm.getNow(index) > lastSec){
lastSec = statComm.getNow(index);
if (!tracked){
tracked = true;
firstActive = firstSec;
@ -571,13 +584,13 @@ void Controller::statSession::update(uint64_t index, IPC::statExchange &data){
long long currUp = getUp();
if (currUp - prevUp < 0 || currDown - prevDown < 0){
INFO_MSG("Negative data usage! %lldu/%lldd (u%lld->%lld) in %s over %s, #%lu", currUp - prevUp,
currDown - prevDown, prevUp, currUp, data.streamName().c_str(), data.connector().c_str(), index);
currDown - prevDown, prevUp, currUp, myStream.c_str(), myConnector.c_str(), index);
}else{
if (!noBWCount){
size_t bwMatchOffset = 0;
noBWCount = 1;
while (noBWCountMatches[bwMatchOffset + 16] != 0 && bwMatchOffset < 1700){
if (Socket::matchIPv6Addr(data.host(), std::string(noBWCountMatches + bwMatchOffset, 16),
if (Socket::matchIPv6Addr(statComm.getHost(index), std::string(noBWCountMatches + bwMatchOffset, 16),
noBWCountMatches[bwMatchOffset + 16])){
noBWCount = 2;
break;
@ -599,40 +612,39 @@ void Controller::statSession::update(uint64_t index, IPC::statExchange &data){
}
}
if (currDown + currUp >= COUNTABLE_BYTES){
std::string streamName = data.streamName();
if (sessionType == SESS_UNSET){
if (data.connector() == "INPUT"){
if (myConnector == "INPUT"){
++servInputs;
streamStats[streamName].inputs++;
streamStats[streamName].currIns++;
streamStats[myStream].inputs++;
streamStats[myStream].currIns++;
sessionType = SESS_INPUT;
}else if (data.connector() == "OUTPUT"){
}else if (myConnector == "OUTPUT"){
++servOutputs;
streamStats[streamName].outputs++;
streamStats[streamName].currOuts++;
streamStats[myStream].outputs++;
streamStats[myStream].currOuts++;
sessionType = SESS_OUTPUT;
}else{
++servViewers;
streamStats[streamName].viewers++;
streamStats[streamName].currViews++;
streamStats[myStream].viewers++;
streamStats[myStream].currViews++;
sessionType = SESS_VIEWER;
}
}
// If previous < COUNTABLE_BYTES, we haven't counted any data so far.
// We need to count all the data in that case, otherwise we only count the difference.
if (prevUp + prevDown < COUNTABLE_BYTES){
if (!streamName.size() || streamName[0] == 0){
if (streamStats.count(streamName)){streamStats.erase(streamName);}
if (!myStream.size() || myStream[0] == 0){
if (streamStats.count(myStream)){streamStats.erase(myStream);}
}else{
streamStats[streamName].upBytes += currUp;
streamStats[streamName].downBytes += currDown;
streamStats[myStream].upBytes += currUp;
streamStats[myStream].downBytes += currDown;
}
}else{
if (!streamName.size() || streamName[0] == 0){
if (streamStats.count(streamName)){streamStats.erase(streamName);}
if (!myStream.size() || myStream[0] == 0){
if (streamStats.count(myStream)){streamStats.erase(myStream);}
}else{
streamStats[streamName].upBytes += currUp - prevUp;
streamStats[streamName].downBytes += currDown - prevDown;
streamStats[myStream].upBytes += currUp - prevUp;
streamStats[myStream].downBytes += currDown - prevDown;
}
}
}
@ -642,7 +654,7 @@ Controller::sessType Controller::statSession::getSessType(){
return sessionType;
}
/// Archives the given connection.
/// Archives connection log entries older than the given cutOff point.
void Controller::statSession::wipeOld(uint64_t cutOff){
if (firstSec > cutOff){return;}
firstSec = 0xFFFFFFFFFFFFFFFFull;
@ -673,9 +685,8 @@ void Controller::statSession::wipeOld(uint64_t cutOff){
}
}
void Controller::statSession::ping(const Controller::sessIndex &index, uint64_t disconnectPoint){
void Controller::statSession::dropSession(const Controller::sessIndex &index){
if (!tracked || curConns.size()){return;}
if (lastSec < disconnectPoint){
switch (sessionType){
case SESS_INPUT:
if (streamStats[index.streamName].currIns){streamStats[index.streamName].currIns--;}
@ -696,8 +707,8 @@ void Controller::statSession::ping(const Controller::sessIndex &index, uint64_t
tagStream << "[" << *it << "]";
}
}
Controller::logAccess(index.ID, index.streamName, index.connector, index.host, duration,
getUp(), getDown(), tagStream.str());
Controller::logAccess(index.ID, index.streamName, index.connector, index.host, duration, getUp(),
getDown(), tagStream.str());
if (Controller::accesslog.size()){
if (Controller::accesslog == "LOG"){
std::stringstream accessStr;
@ -742,7 +753,6 @@ void Controller::statSession::ping(const Controller::sessIndex &index, uint64_t
wipedDown = 0;
oldConns.clear();
sessionType = SESS_UNSET;
}
}
/// Archives the given connection.
@ -836,16 +846,12 @@ bool Controller::statSession::hasDataFor(uint64_t t){
/// Returns true if there is any data for this session.
bool Controller::statSession::hasData(){
if (!firstSec && !lastSec){return false;}
if (curConns.size()){return true;}
if (oldConns.size()){
for (std::deque<statStorage>::iterator it = oldConns.begin(); it != oldConns.end(); ++it){
if (it->log.size()){return true;}
}
}
if (curConns.size()){
for (std::map<uint64_t, statStorage>::iterator it = curConns.begin(); it != curConns.end(); ++it){
if (it->second.log.size()){return true;}
}
}
return false;
}
@ -854,26 +860,14 @@ bool Controller::statSession::isViewerOn(uint64_t t){
return getUp(t) + getDown(t) > COUNTABLE_BYTES;
}
/// Returns true if this session should count as a viewer
bool Controller::statSession::isViewer(){
long long upTotal = wipedUp + wipedDown;
if (oldConns.size()){
for (std::deque<statStorage>::iterator it = oldConns.begin(); it != oldConns.end(); ++it){
if (it->log.size()){
upTotal += it->log.rbegin()->second.up + it->log.rbegin()->second.down;
if (upTotal > COUNTABLE_BYTES){return true;}
}
}
}
if (curConns.size()){
for (std::map<uint64_t, statStorage>::iterator it = curConns.begin(); it != curConns.end(); ++it){
if (it->second.log.size()){
upTotal += it->second.log.rbegin()->second.up + it->second.log.rbegin()->second.down;
if (upTotal > COUNTABLE_BYTES){return true;}
}
}
}
return false;
/// Returns true if this session should be considered connected
bool Controller::statSession::isConnected(){
return curConns.size();
}
/// Returns true if this session has started (tracked == true) but not yet ended (log entry written)
bool Controller::statSession::isTracked(){
return tracked;
}
/// Returns the cumulative connected time for this session at timestamp t.
@ -1014,58 +1008,60 @@ Controller::statLog &Controller::statStorage::getDataFor(unsigned long long t){
/// This function is called by parseStatistics.
/// It updates the internally saved statistics data.
void Controller::statStorage::update(IPC::statExchange &data){
void Controller::statStorage::update(Comms::Statistics &statComm, size_t index){
statLog tmp;
tmp.time = data.time();
tmp.lastSecond = data.lastSecond();
tmp.down = data.down();
tmp.up = data.up();
log[data.now()] = tmp;
tmp.time = statComm.getTime(index);
tmp.lastSecond = statComm.getLastSecond(index);
tmp.down = statComm.getDown(index);
tmp.up = statComm.getUp(index);
log[statComm.getNow(index)] = tmp;
// wipe data older than approx. STAT_CUTOFF seconds
/// \todo Remove least interesting data first.
if (log.size() > STAT_CUTOFF){log.erase(log.begin());}
}
/// This function is called by the shared memory page that holds statistics.
/// It updates the internally saved statistics data, moving across sessions or archiving when necessary.
void Controller::parseStatistics(char *data, size_t len, uint32_t id){
// retrieve stats data
IPC::statExchange tmpEx(data);
void Controller::statLeadIn(){
statDropoff = Util::bootSecs() - 3;
}
void Controller::statOnActive(size_t id){
// calculate the current session index, store as idx.
sessIndex idx(tmpEx);
sessIndex idx(statComm, id);
if (statComm.getNow(id) >= statDropoff){
// if the connection was already indexed and it has changed, move it
if (connToSession.count(id) && connToSession[id] != idx){
if (sessions[connToSession[id]].getSessType() != SESS_UNSET){
INFO_MSG("Switching connection %" PRIu32 " from active session %s over to %s", id,
INFO_MSG("Switching connection %zu from active session %s over to %s", id,
connToSession[id].toStr().c_str(), idx.toStr().c_str());
}else{
INFO_MSG("Switching connection %" PRIu32 " from inactive session %s over to %s", id,
INFO_MSG("Switching connection %zu from inactive session %s over to %s", id,
connToSession[id].toStr().c_str(), idx.toStr().c_str());
}
sessions[connToSession[id]].switchOverTo(sessions[idx], id);
// Destroy this session without calling dropSession, because it was merged into another. What session? We never made it. Stop asking hard questions. Go, shoo. *sprays water*
if (!sessions[connToSession[id]].hasData()){sessions.erase(connToSession[id]);}
}
if (!connToSession.count(id)){
INSANE_MSG("New connection: %" PRIu32 " as %s", id, idx.toStr().c_str());
INSANE_MSG("New connection: %zu as %s", id, idx.toStr().c_str());
}
// store the index for later comparison
connToSession[id] = idx;
// update the session with the latest data
sessions[idx].update(id, tmpEx);
// check validity of stats data
char counter = (*(data - 1)) & 0x7F;
if (counter == 126 || counter == 127){
// the data is no longer valid - connection has gone away, store for later
INSANE_MSG("Ended connection: %" PRIu32 " as %s", id, idx.toStr().c_str());
sessions[idx].finish(id);
connToSession.erase(id);
sessions[idx].update(id, statComm);
}
}
void Controller::statOnDisconnect(size_t id){
sessIndex idx(statComm, id);
INSANE_MSG("Ended connection: %zu as %s", id, idx.toStr().c_str());
sessions[idx].finish(id);
connToSession.erase(id);
}
void Controller::statLeadOut(){}
/// Returns true if this stream has at least one connected client.
bool Controller::hasViewers(std::string streamName){
if (sessions.size()){
long long currTime = Util::epoch();
long long currTime = Util::bootSecs();
for (std::map<sessIndex, statSession>::iterator it = sessions.begin(); it != sessions.end(); it++){
if (it->first.streamName == streamName &&
(it->second.hasDataFor(currTime) || it->second.hasDataFor(currTime - 1))){
@ -1119,7 +1115,11 @@ void Controller::fillClients(JSON::Value &req, JSON::Value &rep){
// to make sure no nasty timing business takes place, we store the case "now" as a bool.
bool now = (reqTime == 0);
// add the current time, if negative or zero.
if (reqTime <= 0){reqTime += Util::epoch();}
if (reqTime <= 0){
reqTime += Util::bootSecs();
}else{
reqTime -= (Util::epoch() - Util::bootSecs());
}
// at this point, reqTime is the absolute timestamp.
rep["time"] = reqTime; // fill the absolute timestamp
@ -1136,6 +1136,7 @@ void Controller::fillClients(JSON::Value &req, JSON::Value &rep){
if ((*it).asStringRef() == "up"){fields |= STAT_CLI_UP;}
if ((*it).asStringRef() == "downbps"){fields |= STAT_CLI_BPS_DOWN;}
if ((*it).asStringRef() == "upbps"){fields |= STAT_CLI_BPS_UP;}
if ((*it).asStringRef() == "sessid"){fields |= STAT_CLI_SESSID;}
}
}
// select all, if none selected
@ -1162,6 +1163,7 @@ void Controller::fillClients(JSON::Value &req, JSON::Value &rep){
if (fields & STAT_CLI_BPS_DOWN){rep["fields"].append("downbps");}
if (fields & STAT_CLI_BPS_UP){rep["fields"].append("upbps");}
if (fields & STAT_CLI_CRC){rep["fields"].append("crc");}
if (fields & STAT_CLI_SESSID){rep["fields"].append("sessid");}
// output the data itself
rep["data"].null();
// loop over all sessions
@ -1185,6 +1187,7 @@ void Controller::fillClients(JSON::Value &req, JSON::Value &rep){
if (fields & STAT_CLI_BPS_DOWN){d.append(it->second.getBpsDown(time));}
if (fields & STAT_CLI_BPS_UP){d.append(it->second.getBpsUp(time));}
if (fields & STAT_CLI_CRC){d.append(it->first.crc);}
if (fields & STAT_CLI_SESSID){d.append(it->first.ID);}
rep["data"].append(d);
}
}
@ -1235,8 +1238,8 @@ void Controller::fillActive(JSON::Value &req, JSON::Value &rep, bool onlyNow){
// collect the data first
std::set<std::string> streams;
std::map<std::string, uint64_t> clients;
unsigned int tOut = Util::epoch() - STATS_DELAY;
unsigned int tIn = Util::epoch() - STATS_INPUT_DELAY;
uint64_t tOut = Util::bootSecs() - STATS_DELAY;
uint64_t tIn = Util::bootSecs() - STATS_INPUT_DELAY;
// check all sessions
{
tthread::lock_guard<tthread::mutex> guard(statsMutex);
@ -1263,26 +1266,14 @@ void Controller::fillActive(JSON::Value &req, JSON::Value &rep, bool onlyNow){
jsonForEach(req, j){
if (j->asStringRef() == "clients"){rep[*it].append(clients[*it]);}
if (j->asStringRef() == "lastms"){
char pageId[NAME_BUFFER_SIZE];
IPC::sharedPage streamIndex;
snprintf(pageId, NAME_BUFFER_SIZE, SHM_STREAM_INDEX, it->c_str());
streamIndex.init(pageId, DEFAULT_STRM_PAGE_SIZE, false, false);
if (streamIndex.mapped){
static char liveSemName[NAME_BUFFER_SIZE];
snprintf(liveSemName, NAME_BUFFER_SIZE, SEM_LIVE, it->c_str());
IPC::semaphore metaLocker(liveSemName, O_CREAT | O_RDWR, (S_IRWXU | S_IRWXG | S_IRWXO), 8);
metaLocker.wait();
DTSC::Scan strm = DTSC::Packet(streamIndex.mapped, streamIndex.len, true).getScan();
DTSC::Meta M(*it, false);
if (M){
uint64_t lms = 0;
DTSC::Scan trcks = strm.getMember("tracks");
unsigned int trcks_ctr = trcks.getSize();
for (unsigned int i = 0; i < trcks_ctr; ++i){
if (trcks.getIndice(i).getMember("lastms").asInt() > lms){
lms = trcks.getIndice(i).getMember("lastms").asInt();
}
std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator jt = validTracks.begin(); jt != validTracks.end(); jt++){
if (M.getLastms(*jt) > lms){lms = M.getLastms(*jt);}
}
rep[*it].append(lms);
metaLocker.post();
}else{
rep[*it].append(-1);
}
@ -1330,9 +1321,9 @@ void Controller::fillTotals(JSON::Value &req, JSON::Value &rep){
if (req.isMember("start")){reqStart = req["start"].asInt();}
if (req.isMember("end")){reqEnd = req["end"].asInt();}
// add the current time, if negative or zero.
if (reqStart < 0){reqStart += Util::epoch();}
if (reqStart == 0){reqStart = Util::epoch() - STAT_CUTOFF;}
if (reqEnd <= 0){reqEnd += Util::epoch();}
if (reqStart < 0){reqStart += Util::bootSecs();}
if (reqStart == 0){reqStart = Util::bootSecs() - STAT_CUTOFF;}
if (reqEnd <= 0){reqEnd += Util::bootSecs();}
// at this point, reqStart and reqEnd are the absolute timestamp.
unsigned int fields = 0;
@ -1458,7 +1449,7 @@ void Controller::handlePrometheus(HTTP::Parser &H, Socket::Connection &conn, int
unsigned long long c_user, c_nice, c_syst, c_idle, c_total;
if (sscanf(line, "cpu %Lu %Lu %Lu %Lu", &c_user, &c_nice, &c_syst, &c_idle) == 4){
c_total = c_user + c_nice + c_syst + c_idle;
if (c_total - cl_total > 0){
if (c_total > cl_total){
cpu_use = (long long int)(1000 - ((c_idle - cl_idle) * 1000) / (c_total - cl_total));
}else{
cpu_use = 0;
@ -1556,8 +1547,8 @@ void Controller::handlePrometheus(HTTP::Parser &H, Socket::Connection &conn, int
// collect the data first
std::map<std::string, uint32_t> outputs;
unsigned long totViewers = 0, totInputs = 0, totOutputs = 0;
unsigned int tOut = Util::epoch() - STATS_DELAY;
unsigned int tIn = Util::epoch() - STATS_INPUT_DELAY;
unsigned int tOut = Util::bootSecs() - STATS_DELAY;
unsigned int tIn = Util::bootSecs() - STATS_INPUT_DELAY;
// check all sessions
if (sessions.size()){
for (std::map<sessIndex, statSession>::iterator it = sessions.begin(); it != sessions.end(); it++){
@ -1629,8 +1620,7 @@ void Controller::handlePrometheus(HTTP::Parser &H, Socket::Connection &conn, int
<< it->second.currOuts << "\n";
response << "mist_viewcount{stream=\"" << it->first << "\"}" << it->second.viewers << "\n";
response << "mist_bw{stream=\"" << it->first << "\",direction=\"up\"}" << it->second.upBytes << "\n";
response << "mist_bw{stream=\"" << it->first << "\",direction=\"down\"}"
<< it->second.downBytes << "\n";
response << "mist_bw{stream=\"" << it->first << "\",direction=\"down\"}" << it->second.downBytes << "\n";
}
}
H.Chunkify(response.str(), conn);
@ -1657,8 +1647,8 @@ void Controller::handlePrometheus(HTTP::Parser &H, Socket::Connection &conn, int
// collect the data first
std::map<std::string, uint32_t> outputs;
uint64_t totViewers = 0, totInputs = 0, totOutputs = 0;
uint64_t tOut = Util::epoch() - STATS_DELAY;
uint64_t tIn = Util::epoch() - STATS_INPUT_DELAY;
uint64_t tOut = Util::bootSecs() - STATS_DELAY;
uint64_t tIn = Util::bootSecs() - STATS_INPUT_DELAY;
// check all sessions
if (sessions.size()){
for (std::map<sessIndex, statSession>::iterator it = sessions.begin(); it != sessions.end(); it++){

View file

@ -1,5 +1,6 @@
#pragma once
#include <map>
#include <mist/comms.h>
#include <mist/defines.h>
#include <mist/http_parser.h>
#include <mist/json.h>
@ -37,9 +38,8 @@ namespace Controller{
/// Whenever two of these objects are not equal, it will create a new session.
class sessIndex{
public:
sessIndex(std::string host, unsigned int crc, std::string streamName, std::string connector);
sessIndex(IPC::statExchange &data);
sessIndex();
sessIndex(const Comms::Statistics &statComm, size_t id);
std::string ID;
std::string host;
unsigned int crc;
@ -57,7 +57,7 @@ namespace Controller{
class statStorage{
public:
void update(IPC::statExchange &data);
void update(Comms::Statistics &statComm, size_t index);
bool hasDataFor(unsigned long long);
statLog &getDataFor(unsigned long long);
std::map<unsigned long long, statLog> log;
@ -87,12 +87,13 @@ namespace Controller{
void wipeOld(uint64_t);
void finish(uint64_t index);
void switchOverTo(statSession &newSess, uint64_t index);
void update(uint64_t index, IPC::statExchange &data);
void ping(const sessIndex &index, uint64_t disconnectPoint);
void update(uint64_t index, Comms::Statistics &data);
void dropSession(const sessIndex &index);
uint64_t getStart();
uint64_t getEnd();
bool isViewerOn(uint64_t time);
bool isViewer();
bool isConnected();
bool isTracked();
bool hasDataFor(uint64_t time);
bool hasData();
uint64_t getConnTime(uint64_t time);
@ -110,6 +111,7 @@ namespace Controller{
extern std::map<sessIndex, statSession> sessions;
extern std::map<unsigned long, sessIndex> connToSession;
extern tthread::mutex statsMutex;
extern uint64_t statDropoff;
struct triggerLog{
uint64_t totalCount;
@ -119,8 +121,12 @@ namespace Controller{
extern std::map<std::string, triggerLog> triggerStats;
void statLeadIn();
void statOnActive(size_t id);
void statOnDisconnect(size_t id);
void statLeadOut();
std::set<std::string> getActiveStreams(const std::string &prefix = "");
void parseStatistics(char *data, size_t len, unsigned int id);
void killStatistics(char *data, size_t len, unsigned int id);
void fillClients(JSON::Value &req, JSON::Value &rep);
void fillActive(JSON::Value &req, JSON::Value &rep, bool onlyNow = false);

View file

@ -139,7 +139,11 @@ namespace Controller{
void initState(){
tthread::lock_guard<tthread::mutex> guard(logMutex);
shmLogs = new IPC::sharedPage(SHM_STATE_LOGS, 1024 * 1024, false, false); // max 1M of logs cached
if (!shmLogs || !shmLogs->mapped){
if (shmLogs){delete shmLogs;}
shmLogs = new IPC::sharedPage(SHM_STATE_LOGS, 1024 * 1024, true); // max 1M of logs cached
}
if (!shmLogs->mapped){
FAIL_MSG("Could not open memory page for logs buffer");
return;
@ -156,7 +160,11 @@ namespace Controller{
}
maxLogsRecs = (1024 * 1024 - rlxLogs->getOffset()) / rlxLogs->getRSize();
shmAccs = new IPC::sharedPage(SHM_STATE_ACCS, 1024 * 1024, false, false); // max 1M of accesslogs cached
if (!shmAccs || !shmAccs->mapped){
if (shmAccs){delete shmAccs;}
shmAccs = new IPC::sharedPage(SHM_STATE_ACCS, 1024 * 1024, true); // max 1M of accesslogs cached
}
if (!shmAccs->mapped){
FAIL_MSG("Could not open memory page for access logs buffer");
return;
@ -176,7 +184,11 @@ namespace Controller{
}
maxAccsRecs = (1024 * 1024 - rlxAccs->getOffset()) / rlxAccs->getRSize();
shmStrm = new IPC::sharedPage(SHM_STATE_STREAMS, 1024 * 1024, false, false); // max 1M of stream data
if (!shmStrm || !shmStrm->mapped){
if (shmStrm){delete shmStrm;}
shmStrm = new IPC::sharedPage(SHM_STATE_STREAMS, 1024 * 1024, true); // max 1M of stream data
}
if (!shmStrm->mapped){
FAIL_MSG("Could not open memory page for stream data");
return;

View file

@ -295,8 +295,8 @@ namespace Controller{
Util::sanitizeName(cleaned);
std::string strmSource;
if (Util::getStreamStatus(cleaned) != STRMSTAT_OFF){
DTSC::Meta mData = Util::getStreamMeta(cleaned);
if (mData.sourceURI.size()){strmSource = mData.sourceURI;}
DTSC::Meta M(cleaned, false);
if (M && M.getSource().size()){strmSource = M.getSource();}
}
if (!strmSource.size()){
std::string smp = cleaned.substr(0, cleaned.find_first_of("+ "));

View file

@ -24,30 +24,6 @@
#define SHARED_SECRET "empty"
#endif
static std::string readFile(std::string filename){
std::ifstream file(filename.c_str());
if (!file.good()){return "";}
file.seekg(0, std::ios::end);
unsigned int len = file.tellg();
file.seekg(0, std::ios::beg);
std::string out;
out.reserve(len);
unsigned int i = 0;
while (file.good() && i++ < len){out += file.get();}
file.close();
return out;
}
static bool writeFile(std::string filename, std::string &contents){
unlink(filename.c_str());
std::ofstream file(filename.c_str(), std::ios_base::trunc | std::ios_base::out);
if (!file.is_open()){return false;}
file << contents;
file.close();
chmod(filename.c_str(), S_IRWXU | S_IRWXG);
return true;
}
tthread::mutex updaterMutex;
uint8_t updatePerc = 0;
JSON::Value updates;

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,11 @@
#include <cstdlib>
#include <fstream>
#include <map>
#include <mist/bitfields.h>
#include <mist/config.h>
#include <mist/defines.h>
#include <mist/dtsc.h>
#include <mist/encryption.h>
#include <mist/json.h>
#include <mist/shared_memory.h>
#include <mist/timing.h>
@ -13,9 +15,9 @@
namespace Mist{
struct booking{
int first;
int curKey;
int curPart;
uint32_t first;
uint32_t curKey;
uint32_t curPart;
};
class Input : public InOutBase{
@ -26,61 +28,63 @@ namespace Mist{
virtual int boot(int argc, char *argv[]);
virtual ~Input(){};
bool keepAlive();
void reloadClientMeta();
bool hasMeta() const;
static Util::Config *config;
virtual bool needsLock(){return !config->getBool("realtime");}
protected:
static void callbackWrapper(char *data, size_t len, unsigned int id);
virtual bool checkArguments() = 0;
virtual bool readHeader() = 0;
virtual bool readHeader();
virtual bool needHeader(){return !readExistingHeader();}
virtual bool preRun(){return true;}
virtual bool isSingular(){return !config->getBool("realtime");}
virtual bool readExistingHeader();
virtual bool atKeyFrame();
virtual void getNext(bool smart = true){}
virtual void seek(int seekTime){};
virtual void getNext(size_t idx = INVALID_TRACK_ID){}
virtual void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID){}
virtual void finish();
virtual bool keepRunning();
virtual bool openStreamSource(){return readHeader();}
virtual void closeStreamSource(){}
virtual void parseStreamHeader(){}
void play(int until = 0);
void playOnce();
void quitPlay();
void checkHeaderTimes(std::string streamFile);
virtual void removeUnused();
virtual void trackSelect(std::string trackSpec);
virtual void userCallback(char *data, size_t len, unsigned int id);
virtual void convert();
virtual void serve();
virtual void stream();
virtual size_t streamByteCount(){
return 0;
}; // For live streams: to update the stats with correct values.
virtual std::string streamMainLoop();
virtual std::string realtimeMainLoop();
bool isAlwaysOn();
virtual void userLeadIn();
virtual void userOnActive(size_t id);
virtual void userOnDisconnect(size_t id);
virtual void userLeadOut();
virtual void parseHeader();
bool bufferFrame(unsigned int track, unsigned int keyNum);
bool bufferFrame(size_t track, uint32_t keyNum);
unsigned int packTime; /// Media-timestamp of the last packet.
int lastActive; /// Timestamp of the last time we received or sent something.
int initialTime;
int playing;
unsigned int playUntil;
bool isBuffer;
uint64_t activityCounter;
JSON::Value capa;
std::map<int, std::set<int> > keyTimes;
int64_t timeOffset;
std::map<size_t, std::set<uint64_t> > keyTimes;
// Create server for user pages
IPC::sharedServer userPage;
Comms::Users users;
size_t connectedUsers;
Encryption::AES aesCipher;
IPC::sharedPage streamStatus;
std::map<unsigned int, std::map<unsigned int, unsigned int> > pageCounter;
std::map<size_t, std::map<uint32_t, size_t> > pageCounter;
static Input *singleton;
@ -93,6 +97,7 @@ namespace Mist{
DTSC::Packet srtPack;
uint64_t simStartTime;
};
void handleBuyDRM();
};
}// namespace Mist

View file

@ -72,7 +72,7 @@ namespace Mist{
if (ret != 0){
char errstr[300];
av_strerror(ret, errstr, 300);
DEBUG_MSG(DLVL_FAIL, "Could not open file: %s", errstr);
FAIL_MSG("Could not open file: %s", errstr);
return false; // Couldn't open file
}
@ -81,7 +81,7 @@ namespace Mist{
if (ret < 0){
char errstr[300];
av_strerror(ret, errstr, 300);
DEBUG_MSG(DLVL_FAIL, "Could not find stream info: %s", errstr);
FAIL_MSG("Could not find stream info: %s", errstr);
return false;
}
return true;
@ -160,12 +160,12 @@ namespace Mist{
return true;
}
void inputAV::getNext(bool smart){
void inputAV::getNext(){
AVPacket packet;
while (av_read_frame(pFormatCtx, &packet) >= 0){
// filter tracks we don't care about
if (!selectedTracks.count(packet.stream_index + 1)){
DEBUG_MSG(DLVL_HIGH, "Track %u not selected", packet.stream_index + 1);
HIGH_MSG("Track %u not selected", packet.stream_index + 1);
continue;
}
AVStream *strm = pFormatCtx->streams[packet.stream_index];
@ -187,7 +187,7 @@ namespace Mist{
thisPacket.null();
preRun();
// failure :-(
DEBUG_MSG(DLVL_FAIL, "getNext failed");
FAIL_MSG("getNext failed");
}
void inputAV::seek(int seekTime){

View file

@ -23,7 +23,7 @@ namespace Mist{
bool checkArguments();
bool preRun();
bool readHeader();
void getNext(bool smart = true);
void getNext();
void seek(int seekTime);
void trackSelect(std::string trackSpec);

View file

@ -47,7 +47,7 @@ namespace Mist{
Socket::Connection balConn(url.host, url.getPort(), true);
if (!balConn){
WARN_MSG("Failed to reach %s on port %lu", url.host.c_str(), url.getPort());
WARN_MSG("Failed to reach %s on port %" PRIu16, url.host.c_str(), url.getPort());
}else{
HTTP::Parser http;
http.url = "/" + url.path;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
#include <fstream>
#include "input.h"
#include <fstream>
#include <mist/dtsc.h>
#include <mist/shared_memory.h>
@ -12,11 +11,12 @@ namespace Mist{
void onCrash();
private:
void fillBufferDetails(JSON::Value &details);
unsigned int bufferTime;
unsigned int cutTime;
unsigned int segmentSize; /*LTS*/
unsigned int lastReTime; /*LTS*/
void fillBufferDetails(JSON::Value &details) const;
uint64_t bufferTime;
uint64_t cutTime;
size_t segmentSize; /*LTS*/
uint64_t lastReTime; /*LTS*/
uint64_t finalMillis;
bool hasPush;
bool resumeMode;
IPC::semaphore *liveMeta;
@ -28,29 +28,30 @@ namespace Mist{
void updateMeta();
bool readHeader(){return false;}
bool needHeader(){return false;}
void getNext(bool smart = true){}
void updateTrackMeta(unsigned long tNum);
void updateMetaFromPage(unsigned long tNum, unsigned long pageNum);
void seek(int seekTime){}
void trackSelect(std::string trackSpec){}
bool removeKey(unsigned int tid);
void getNext(size_t idx = INVALID_TRACK_ID){};
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID){};
void removeTrack(size_t tid);
bool removeKey(size_t tid);
void removeUnused();
void eraseTrackDataPages(unsigned long tid);
void finish();
void userCallback(char *data, size_t len, unsigned int id);
std::set<unsigned long> negotiatingTracks;
std::set<unsigned long> activeTracks;
std::map<unsigned long, unsigned long long> lastUpdated;
std::map<unsigned long, unsigned long long> negotiationTimeout;
/// Maps trackid to a pagenum->pageData map
std::map<unsigned long, std::map<unsigned long, DTSCPageData> > bufferLocations;
std::map<unsigned long, char *> pushLocation;
inputBuffer *singleton;
uint64_t retrieveSetting(DTSC::Scan &streamCfg, const std::string &setting, const std::string &option = "");
void userLeadIn();
void userOnActive(size_t id);
void userOnDisconnect(size_t id);
void userLeadOut();
// This is used for an ugly fix to prevent metadata from disappearing in some cases.
std::map<unsigned long, std::string> initData;
std::map<size_t, std::string> initData;
uint64_t findTrack(const std::string &trackVal);
void checkProcesses(const JSON::Value &procs); // LTS
std::map<std::string, pid_t> runningProcs; // LTS
std::set<size_t> generatePids;
std::map<size_t, std::set<size_t> > sourcePids;
};
}// namespace Mist

View file

@ -60,10 +60,19 @@ namespace Mist{
capa["optional"]["segmentsize"]["type"] = "uint";
capa["optional"]["segmentsize"]["default"] = 1900;
/*LTS-END*/
F = NULL;
lockCache = false;
lockNeeded = false;
}
bool inputDTSC::needsLock(){
return config->getString("input").substr(0, 7) != "dtsc://" && config->getString("input") != "-";
if (!lockCache){
lockNeeded =
config->getString("input").substr(0, 7) != "dtsc://" && config->getString("input") != "-";
lockCache = true;
}
return lockNeeded;
}
void parseDTSCURI(const std::string &src, std::string &host, uint16_t &port,
@ -129,38 +138,41 @@ namespace Mist{
void inputDTSC::parseStreamHeader(){
while (srcConn.connected() && config->is_active){
srcConn.spool();
if (srcConn.Received().available(8)){
if (srcConn.Received().copy(4) == "DTCM" || srcConn.Received().copy(4) == "DTSC"){
if (!srcConn.Received().available(8)){
Util::sleep(100);
keepAlive();
continue;
}
if (srcConn.Received().copy(4) != "DTCM" && srcConn.Received().copy(4) != "DTSC"){
INFO_MSG("Received a wrong type of packet - '%s'", srcConn.Received().copy(4).c_str());
break;
}
// Command message
std::string toRec = srcConn.Received().copy(8);
unsigned long rSize = Bit::btohl(toRec.c_str() + 4);
uint32_t rSize = Bit::btohl(toRec.c_str() + 4);
if (!srcConn.Received().available(8 + rSize)){
nProxy.userClient.keepAlive();
keepAlive();
Util::sleep(100);
continue; // abort - not enough data yet
}
// Ignore initial DTCM message, as this is a "hi" message from the server
if (srcConn.Received().copy(4) == "DTCM"){
srcConn.Received().remove(8 + rSize);
}else{
continue;
}
std::string dataPacket = srcConn.Received().remove(8 + rSize);
DTSC::Packet metaPack(dataPacket.data(), dataPacket.size());
myMeta.reinit(metaPack);
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin();
it != myMeta.tracks.end(); it++){
continueNegotiate(it->first, true);
DTSC::Meta nM("", metaPack.getScan());
meta.reInit(streamName, false);
meta.merge(nM);
std::set<size_t> validTracks = M.getMySourceTracks(getpid());
userSelect.clear();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); ++it){
userSelect[*it].reload(streamName, *it, COMM_STATUS_SOURCE | COMM_STATUS_DONOTTRACK);
}
break;
}
}else{
INFO_MSG("Received a wrong type of packet - '%s'", srcConn.Received().copy(4).c_str());
break;
}
}else{
Util::sleep(100);
nProxy.userClient.keepAlive();
}
}
}
bool inputDTSC::openStreamSource(){
@ -194,9 +206,7 @@ namespace Mist{
void inputDTSC::closeStreamSource(){srcConn.close();}
bool inputDTSC::checkArguments(){
if (!needsLock()){
return true;
}else{
if (!needsLock()){return true;}
if (!config->getString("streamname").size()){
if (config->getString("output") == "-"){
std::cerr << "Output to stdout not yet supported" << std::endl;
@ -210,9 +220,12 @@ namespace Mist{
}
// open File
inFile = DTSC::File(config->getString("input"));
if (!inFile){return false;}
F = fopen(config->getString("input").c_str(), "r+b");
if (!F){
HIGH_MSG("Could not open file %s", config->getString("input").c_str());
return false;
}
fseek(F, 0, SEEK_SET);
return true;
}
@ -222,120 +235,215 @@ namespace Mist{
}
bool inputDTSC::readHeader(){
if (!inFile){return false;}
if (inFile.getMeta().moreheader < 0 || inFile.getMeta().tracks.size() == 0){
DEBUG_MSG(DLVL_FAIL, "Missing external header file");
if (!F){return false;}
if (!readExistingHeader()){
size_t moreHeader = 0;
do{
// read existing header from file here?
char hdr[8];
fseek(F, moreHeader, SEEK_SET);
if (fread(hdr, 8, 1, F) != 1){
FAIL_MSG("Could not read header @ bpos %zu", moreHeader);
return false;
}
myMeta = DTSC::Meta(inFile.getMeta());
DEBUG_MSG(DLVL_DEVEL, "Meta read in with %lu tracks", myMeta.tracks.size());
return true;
if (memcmp(hdr, DTSC::Magic_Header, 4)){
FAIL_MSG("File does not have a DTSC header @ bpos %zu", moreHeader);
return false;
}
size_t pktLen = Bit::btohl(hdr + 4);
char *pkt = (char *)malloc(8 + pktLen * sizeof(char));
fseek(F, moreHeader, SEEK_SET);
if (fread(pkt, 8 + pktLen, 1, F) != 1){
free(pkt);
FAIL_MSG("Could not read packet @ bpos %zu", moreHeader);
}
DTSC::Scan S(pkt + 8, pktLen);
if (S.hasMember("moreheader") && S.getMember("moreheader").asInt()){
moreHeader = S.getMember("moreheader").asInt();
}else{
moreHeader = 0;
meta.reInit(streamName, moreHeader);
}
void inputDTSC::getNext(bool smart){
free(pkt);
}while (moreHeader);
}
return meta;
}
void inputDTSC::getNext(size_t idx){
if (!needsLock()){
getNextFromStream(idx);
return;
}
if (!currentPositions.size()){
WARN_MSG("No seek positions set - returning empty packet.");
thisPacket.null();
return;
}
seekPos thisPos = *currentPositions.begin();
fseek(F, thisPos.bytePos, SEEK_SET);
if (feof(F)){
thisPacket.null();
return;
}
clearerr(F);
currentPositions.erase(currentPositions.begin());
lastreadpos = ftell(F);
if (fread(buffer, 4, 1, F) != 1){
if (feof(F)){
INFO_MSG("End of file reached while seeking @ %" PRIu64, lastreadpos);
}else{
ERROR_MSG("Could not seek to next @ %" PRIu64, lastreadpos);
}
thisPacket.null();
return;
}
if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){
seekNext(thisPacket.getTime(), thisPacket.getTrackId(), true);
getNext(idx);
return;
}
uint8_t version = 0;
if (memcmp(buffer, DTSC::Magic_Packet, 4) == 0){version = 1;}
if (memcmp(buffer, DTSC::Magic_Packet2, 4) == 0){version = 2;}
if (version == 0){
ERROR_MSG("Invalid packet header @ %#" PRIx64 " - %.4s != %.4s @ %" PRIu64, lastreadpos,
buffer, DTSC::Magic_Packet2, lastreadpos);
thisPacket.null();
return;
}
if (fread(buffer + 4, 4, 1, F) != 1){
ERROR_MSG("Could not read packet size @ %" PRIu64, lastreadpos);
thisPacket.null();
return;
}
std::string pBuf;
uint32_t packSize = Bit::btohl(buffer + 4);
pBuf.resize(8 + packSize);
memcpy((char *)pBuf.data(), buffer, 8);
if (fread((void *)(pBuf.data() + 8), packSize, 1, F) != 1){
ERROR_MSG("Could not read packet @ %" PRIu64, lastreadpos);
thisPacket.null();
return;
}
thisPacket.reInit(pBuf.data(), pBuf.size());
seekNext(thisPos.seekTime, thisPos.trackID);
fseek(F, thisPos.bytePos, SEEK_SET);
}
void inputDTSC::getNextFromStream(size_t idx){
thisPacket.reInit(srcConn);
while (config->is_active){
if (thisPacket.getVersion() == DTSC::DTCM){
nProxy.userClient.keepAlive();
// userClient.keepAlive();
std::string cmd;
thisPacket.getString("cmd", cmd);
if (cmd == "reset"){
if (cmd != "reset"){
thisPacket.reInit(srcConn);
continue;
}
// Read next packet
thisPacket.reInit(srcConn);
if (thisPacket.getVersion() != DTSC::DTSC_HEAD){
meta.clear();
continue;
}
DTSC::Meta nM("", thisPacket.getScan());
meta.merge(nM, true, false);
thisPacket.reInit(srcConn); // read the next packet before continuing
continue; // parse the next packet before returning
}
if (thisPacket.getVersion() == DTSC::DTSC_HEAD){
DTSC::Meta newMeta;
newMeta.reinit(thisPacket);
// Detect new tracks
std::set<unsigned int> newTracks;
for (std::map<unsigned int, DTSC::Track>::iterator it = newMeta.tracks.begin();
it != newMeta.tracks.end(); it++){
if (!myMeta.tracks.count(it->first)){newTracks.insert(it->first);}
}
for (std::set<unsigned int>::iterator it = newTracks.begin(); it != newTracks.end(); it++){
INFO_MSG("Reset: adding track %d", *it);
myMeta.tracks[*it] = newMeta.tracks[*it];
continueNegotiate(*it, true);
}
// Detect removed tracks
std::set<unsigned int> deletedTracks;
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin();
it != myMeta.tracks.end(); it++){
if (!newMeta.tracks.count(it->first)){deletedTracks.insert(it->first);}
}
for (std::set<unsigned int>::iterator it = deletedTracks.begin();
it != deletedTracks.end(); it++){
INFO_MSG("Reset: deleting track %d", *it);
myMeta.tracks.erase(*it);
}
thisPacket.reInit(srcConn); // read the next packet before continuing
}else{
myMeta = DTSC::Meta();
}
}else{
thisPacket.reInit(srcConn); // read the next packet before continuing
}
continue; // parse the next packet before returning
}else if (thisPacket.getVersion() == DTSC::DTSC_HEAD){
DTSC::Meta newMeta;
newMeta.reinit(thisPacket);
std::set<unsigned int> newTracks;
for (std::map<unsigned int, DTSC::Track>::iterator it = newMeta.tracks.begin();
it != newMeta.tracks.end(); it++){
if (!myMeta.tracks.count(it->first)){newTracks.insert(it->first);}
}
for (std::set<unsigned int>::iterator it = newTracks.begin(); it != newTracks.end(); it++){
INFO_MSG("New header: adding track %d (%s)", *it, newMeta.tracks[*it].type.c_str());
myMeta.tracks[*it] = newMeta.tracks[*it];
continueNegotiate(*it, true);
}
DTSC::Meta nM("", thisPacket.getScan());
meta.merge(nM, false, false);
thisPacket.reInit(srcConn); // read the next packet before continuing
continue; // parse the next packet before returning
}
// We now know we have either a data packet, or an error.
if (!thisPacket.getTrackId()){
if (thisPacket.getVersion() == DTSC::DTSC_V2){
WARN_MSG("Received bad packet for stream %s: %llu@%llu", streamName.c_str(),
thisPacket.getTrackId(), thisPacket.getTime());
}else{
// All types except data packets are handled above, so if it's not a V2 data packet, we assume corruption
WARN_MSG("Invalid packet header for stream %s", streamName.c_str());
}
}
thisPacket = DTSC::Packet(thisPacket, M.trackIDToIndex(thisPacket.getTrackId(), getpid()));
return; // we have a packet
}
}else{
if (smart){
inFile.seekNext();
}else{
inFile.parseNext();
}
thisPacket = inFile.getPacket();
void inputDTSC::seek(uint64_t seekTime, size_t idx){
currentPositions.clear();
if (idx != INVALID_TRACK_ID){
seekNext(seekTime, idx, true);
}else{
std::set<size_t> tracks = M.getValidTracks();
for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){
seekNext(seekTime, *it, true);
}
}
}
void inputDTSC::seek(int seekTime){
inFile.seek_time(seekTime);
initialTime = 0;
playUntil = 0;
}
void inputDTSC::trackSelect(std::string trackSpec){
selectedTracks.clear();
long long unsigned int index;
while (trackSpec != ""){
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
if (index != std::string::npos){
trackSpec.erase(0, index + 1);
void inputDTSC::seekNext(uint64_t ms, size_t trackIdx, bool forceSeek){
seekPos tmpPos;
tmpPos.trackID = trackIdx;
if (!forceSeek && thisPacket && ms >= thisPacket.getTime() && trackIdx >= thisPacket.getTrackId()){
tmpPos.seekTime = thisPacket.getTime();
tmpPos.bytePos = ftell(F);
}else{
trackSpec = "";
tmpPos.seekTime = 0;
tmpPos.bytePos = 0;
}
if (feof(F)){
clearerr(F);
fseek(F, 0, SEEK_SET);
tmpPos.bytePos = 0;
tmpPos.seekTime = 0;
}
DTSC::Keys keys(M.keys(trackIdx));
uint32_t keyNum = keys.getNumForTime(ms);
if (keys.getTime(keyNum) > tmpPos.seekTime){
tmpPos.seekTime = keys.getTime(keyNum);
tmpPos.bytePos = keys.getBpos(keyNum);
}
bool foundPacket = false;
while (!foundPacket){
lastreadpos = ftell(F);
if (feof(F)){
WARN_MSG("Reached EOF during seek to %" PRIu64 " in track %zu - aborting @ %" PRIu64, ms,
trackIdx, lastreadpos);
return;
}
// Seek to first packet after ms.
fseek(F, tmpPos.bytePos, SEEK_SET);
lastreadpos = ftell(F);
// read the header
char header[20];
if (fread((void *)header, 20, 1, F) != 1){
WARN_MSG("Could not read header from file. Much sadface.");
return;
}
// check if packetID matches, if not, skip size + 8 bytes.
uint32_t packSize = Bit::btohl(header + 4);
uint32_t packID = Bit::btohl(header + 8);
if (memcmp(header, DTSC::Magic_Packet2, 4) != 0 || packID != trackIdx){
if (memcmp(header, "DT", 2) != 0){
WARN_MSG("Invalid header during seek to %" PRIu64 " in track %zu @ %" PRIu64
" - resetting bytePos from %" PRIu64 " to zero",
ms, trackIdx, lastreadpos, tmpPos.bytePos);
tmpPos.bytePos = 0;
continue;
}
tmpPos.bytePos += 8 + packSize;
continue;
}
// get timestamp of packet, if too large, break, if not, skip size bytes.
uint64_t myTime = Bit::btohll(header + 12);
tmpPos.seekTime = myTime;
if (myTime >= ms){
foundPacket = true;
}else{
tmpPos.bytePos += 8 + packSize;
continue;
}
}
inFile.selectTracks(selectedTracks);
// HIGH_MSG("Seek to %u:%d resulted in %lli", trackIdx, ms, tmpPos.seekTime);
if (tmpPos.seekTime > 0xffffffffffffff00ll){tmpPos.seekTime = 0;}
currentPositions.insert(tmpPos);
return;
}
}// namespace Mist

View file

@ -1,7 +1,27 @@
#include "input.h"
#include <set>
#include <stdio.h> //for FILE
#include <mist/dtsc.h>
namespace Mist{
///\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;}
if (seekTime == rhs.seekTime){return trackID < rhs.trackID;}
return false;
}
uint64_t seekTime; ///< Stores the timestamp of the DTSC packet referenced by this structure.
uint64_t bytePos; ///< Stores the byteposition of the DTSC packet referenced by this structure.
uint32_t trackID; ///< Stores the track the DTSC packet referenced by this structure is
///< associated with.
};
class inputDTSC : public Input{
public:
inputDTSC(Util::Config *cfg);
@ -15,13 +35,24 @@ namespace Mist{
bool checkArguments();
bool readHeader();
bool needHeader();
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
void getNext(size_t idx = INVALID_TRACK_ID);
void getNextFromStream(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
DTSC::File inFile;
FILE *F;
Socket::Connection srcConn;
bool lockCache;
bool lockNeeded;
std::set<seekPos> currentPositions;
uint64_t lastreadpos;
char buffer[8];
void seekNext(uint64_t ms, size_t trackIdx, bool forceSeek = false);
};
}// namespace Mist

View file

@ -41,6 +41,7 @@ namespace Mist{
lastClusterTime = 0;
bufferedPacks = 0;
wantBlocks = true;
totalBytes = 0;
}
std::string ASStoSRT(const char *ptr, uint32_t len){
@ -112,13 +113,15 @@ namespace Mist{
uint32_t needed = EBML::Element::needBytes(ptr, ptr.size(), readingMinimal);
while (ptr.size() < needed){
if (!ptr.allocate(needed)){return false;}
if (!fread(ptr + ptr.size(), needed - ptr.size(), 1, inFile)){
int64_t toRead = needed - ptr.size();
if (!fread(ptr + ptr.size(), toRead, 1, inFile)){
// We assume if there is no current data buffered, that we are at EOF and don't print a warning
if (ptr.size()){
FAIL_MSG("Could not read more data! (have %lu, need %lu)", ptr.size(), needed);
FAIL_MSG("Could not read more data! (have %zu, need %" PRIu32 ")", ptr.size(), needed);
}
return false;
}
totalBytes += toRead;
ptr.size() = needed;
needed = EBML::Element::needBytes(ptr, ptr.size(), readingMinimal);
if (ptr.size() >= needed){
@ -141,26 +144,26 @@ namespace Mist{
lastClusterBPos = bp;
}
}
DONTEVEN_MSG("Found a cluster at position %llu", lastClusterBPos);
DONTEVEN_MSG("Found a cluster at position %" PRIu64, lastClusterBPos);
}
if (E.getID() == EBML::EID_TIMECODE){
lastClusterTime = E.getValUInt();
DONTEVEN_MSG("Cluster time %llu ms", lastClusterTime);
DONTEVEN_MSG("Cluster time %" PRIu64 " ms", lastClusterTime);
}
return true;
}
bool InputEBML::readExistingHeader(){
if (!Input::readExistingHeader()){return false;}
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin();
it != myMeta.tracks.end(); ++it){
if (it->second.codec == "PCMLE"){
it->second.codec = "PCM";
swapEndianness.insert(it->first);
std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
if (M.getCodec(*it) == "PCMLE"){
meta.setCodec(*it, "PCM");
swapEndianness.insert(*it);
}
}
if (myMeta.inputLocalVars.isMember("timescale")){
timeScale = ((double)myMeta.inputLocalVars["timescale"].asInt()) / 1000000.0;
if (M.inputLocalVars.isMember("timescale")){
timeScale = ((double)M.inputLocalVars["timescale"].asInt()) / 1000000.0;
}
return true;
}
@ -169,6 +172,7 @@ namespace Mist{
if (!inFile){return false;}
// Create header file from file
uint64_t bench = Util::getMicros();
if (!meta){meta.reInit(streamName);}
while (readElement()){
EBML::Element E(ptr, readingMinimal);
@ -178,7 +182,7 @@ namespace Mist{
ERROR_MSG("Track without track number encountered, ignoring");
continue;
}
uint64_t trackNo = tmpElem.getValUInt();
uint64_t trackID = tmpElem.getValUInt();
tmpElem = E.findChild(EBML::EID_CODECID);
if (!tmpElem){
ERROR_MSG("Track without codec id encountered, ignoring");
@ -311,32 +315,33 @@ namespace Mist{
}
tmpElem = E.findChild(EBML::EID_LANGUAGE);
if (tmpElem){lang = tmpElem.getValString();}
DTSC::Track &Trk = myMeta.tracks[trackNo];
Trk.trackID = trackNo;
Trk.lang = lang;
Trk.codec = trueCodec;
Trk.type = trueType;
Trk.init = init;
if (Trk.type == "video"){
size_t idx = M.trackIDToIndex(trackID, getpid());
if (idx == INVALID_TRACK_ID){idx = meta.addTrack();}
meta.setID(idx, trackID);
meta.setLang(idx, lang);
meta.setCodec(idx, trueCodec);
meta.setType(idx, trueType);
meta.setInit(idx, init);
if (trueType == "video"){
tmpElem = E.findChild(EBML::EID_PIXELWIDTH);
Trk.width = tmpElem ? tmpElem.getValUInt() : 0;
meta.setWidth(idx, tmpElem ? tmpElem.getValUInt() : 0);
tmpElem = E.findChild(EBML::EID_PIXELHEIGHT);
Trk.height = tmpElem ? tmpElem.getValUInt() : 0;
Trk.fpks = 0;
meta.setHeight(idx, tmpElem ? tmpElem.getValUInt() : 0);
meta.setFpks(idx, 0);
}
if (Trk.type == "audio"){
if (trueType == "audio"){
tmpElem = E.findChild(EBML::EID_CHANNELS);
Trk.channels = tmpElem ? tmpElem.getValUInt() : 1;
meta.setChannels(idx, tmpElem ? tmpElem.getValUInt() : 1);
tmpElem = E.findChild(EBML::EID_BITDEPTH);
Trk.size = tmpElem ? tmpElem.getValUInt() : 0;
meta.setSize(idx, tmpElem ? tmpElem.getValUInt() : 0);
tmpElem = E.findChild(EBML::EID_SAMPLINGFREQUENCY);
Trk.rate = tmpElem ? (int)tmpElem.getValFloat() : 8000;
meta.setRate(idx, tmpElem ? (int)tmpElem.getValFloat() : 8000);
}
INFO_MSG("Detected track: %s", Trk.getIdentifier().c_str());
INFO_MSG("Detected track: %s", M.getTrackIdentifier(idx).c_str());
}
if (E.getID() == EBML::EID_TIMECODESCALE){
uint64_t timeScaleVal = E.getValUInt();
myMeta.inputLocalVars["timescale"] = timeScaleVal;
meta.inputLocalVars["timescale"] = timeScaleVal;
timeScale = ((double)timeScaleVal) / 1000000.0;
}
// Live streams stop parsing the header as soon as the first Cluster is encountered
@ -346,34 +351,35 @@ namespace Mist{
uint64_t tNum = B.getTrackNum();
uint64_t newTime = lastClusterTime + B.getTimecode();
trackPredictor &TP = packBuf[tNum];
DTSC::Track &Trk = myMeta.tracks[tNum];
bool isVideo = (Trk.type == "video");
bool isAudio = (Trk.type == "audio");
bool isASS = (Trk.codec == "subtitle" && Trk.init.size());
size_t idx = meta.trackIDToIndex(tNum, getpid());
bool isVideo = (M.getType(idx) == "video");
bool isAudio = (M.getType(idx) == "audio");
bool isASS = (M.getCodec(idx) == "subtitle" && M.getInit(idx).size());
// If this is a new video keyframe, flush the corresponding trackPredictor
if (isVideo && B.isKeyframe()){
while (TP.hasPackets(true)){
packetData &C = TP.getPacketData(true);
myMeta.update(C.time, C.offset, C.track, C.dsize, C.bpos, C.key);
meta.update(C.time, C.offset, C.track, C.dsize, C.bpos, C.key);
TP.remove();
}
TP.flush();
}
for (uint64_t frameNo = 0; frameNo < B.getFrameCount(); ++frameNo){
if (frameNo){
if (Trk.codec == "AAC"){
newTime += (1000000 / Trk.rate) / timeScale; // assume ~1000 samples per frame
}else if (Trk.codec == "MP3"){
newTime += (1152000 / Trk.rate) / timeScale; // 1152 samples per frame
}else if (Trk.codec == "DTS"){
if (M.getCodec(idx) == "AAC"){
newTime += (1000000 / M.getRate(idx)) / timeScale; // assume ~1000 samples per frame
}else if (M.getCodec(idx) == "MP3"){
newTime += (1152000 / M.getRate(idx)) / timeScale; // 1152 samples per frame
}else if (M.getCodec(idx) == "DTS"){
// Assume 512 samples per frame (DVD default)
// actual amount can be calculated from data, but data
// is not available during header generation...
// See: http://www.stnsoft.com/DVD/dtshdr.html
newTime += (512000 / Trk.rate) / timeScale;
newTime += (512000 / M.getRate(idx)) / timeScale;
}else{
newTime += 1 / timeScale;
ERROR_MSG("Unknown frame duration for codec %s - timestamps WILL be wrong!", Trk.codec.c_str());
ERROR_MSG("Unknown frame duration for codec %s - timestamps WILL be wrong!",
M.getCodec(idx).c_str());
}
}
uint32_t frameSize = B.getFrameSize(frameNo);
@ -388,7 +394,7 @@ namespace Mist{
}
while (TP.hasPackets()){
packetData &C = TP.getPacketData(isVideo);
myMeta.update(C.time, C.offset, C.track, C.dsize, C.bpos, C.key);
meta.update(C.time, C.offset, M.trackIDToIndex(C.track, getpid()), C.dsize, C.bpos, C.key);
TP.remove();
}
}
@ -398,23 +404,25 @@ namespace Mist{
for (std::map<uint64_t, trackPredictor>::iterator it = packBuf.begin(); it != packBuf.end(); ++it){
trackPredictor &TP = it->second;
while (TP.hasPackets(true)){
packetData &C = TP.getPacketData(myMeta.tracks[it->first].type == "video");
myMeta.update(C.time, C.offset, C.track, C.dsize, C.bpos, C.key);
packetData &C =
TP.getPacketData(M.getType(M.trackIDToIndex(it->first, getpid())) == "video");
meta.update(C.time, C.offset, M.trackIDToIndex(C.track, getpid()), C.dsize, C.bpos, C.key);
TP.remove();
}
}
}
bench = Util::getMicros(bench);
INFO_MSG("Header generated in %llu ms", bench / 1000);
INFO_MSG("Header generated in %" PRIu64 " ms", bench / 1000);
clearPredictors();
bufferedPacks = 0;
myMeta.toFile(config->getString("input") + ".dtsh");
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin();
it != myMeta.tracks.end(); ++it){
if (it->second.codec == "PCMLE"){
it->second.codec = "PCM";
swapEndianness.insert(it->first);
M.toFile(config->getString("input") + ".dtsh");
std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
if (M.getCodec(*it) == "PCMLE"){
meta.setCodec(*it, "PCM");
swapEndianness.insert(*it);
}
}
return true;
@ -422,7 +430,7 @@ namespace Mist{
void InputEBML::fillPacket(packetData &C){
if (swapEndianness.count(C.track)){
switch (myMeta.tracks[C.track].size){
switch (M.getSize(M.trackIDToIndex(C.track, getpid()))){
case 16:{
char *ptr = C.ptr;
uint32_t ptrSize = C.dsize;
@ -455,16 +463,18 @@ namespace Mist{
}break;
}
}
thisPacket.genericFill(C.time, C.offset, C.track, C.ptr, C.dsize, C.bpos, C.key);
thisPacket.genericFill(C.time, C.offset, M.trackIDToIndex(C.track, getpid()), C.ptr, C.dsize,
C.bpos, C.key);
}
void InputEBML::getNext(bool smart){
void InputEBML::getNext(size_t idx){
// Make sure we empty our buffer first
if (bufferedPacks && packBuf.size()){
for (std::map<uint64_t, trackPredictor>::iterator it = packBuf.begin(); it != packBuf.end(); ++it){
trackPredictor &TP = it->second;
if (TP.hasPackets()){
packetData &C = TP.getPacketData(myMeta.tracks[it->first].type == "video");
packetData &C =
TP.getPacketData(M.getType(M.trackIDToIndex(it->first, getpid())) == "video");
fillPacket(C);
TP.remove();
--bufferedPacks;
@ -481,7 +491,7 @@ namespace Mist{
for (std::map<uint64_t, trackPredictor>::iterator it = packBuf.begin(); it != packBuf.end(); ++it){
trackPredictor &TP = it->second;
if (TP.hasPackets(true)){
packetData &C = TP.getPacketData(myMeta.tracks[it->first].type == "video");
packetData &C = TP.getPacketData(M.getType(M.trackIDToIndex(it->first, getpid())) == "video");
fillPacket(C);
TP.remove();
--bufferedPacks;
@ -494,7 +504,8 @@ namespace Mist{
return;
}
B = EBML::Block(ptr);
}while (!B || B.getType() != EBML::ELEM_BLOCK || !selectedTracks.count(B.getTrackNum()));
}while (!B || B.getType() != EBML::ELEM_BLOCK ||
(idx != INVALID_TRACK_ID && M.getID(idx) != B.getTrackNum()));
}else{
B = EBML::Block(ptr);
}
@ -502,10 +513,10 @@ namespace Mist{
uint64_t tNum = B.getTrackNum();
uint64_t newTime = lastClusterTime + B.getTimecode();
trackPredictor &TP = packBuf[tNum];
DTSC::Track &Trk = myMeta.tracks[tNum];
bool isVideo = (Trk.type == "video");
bool isAudio = (Trk.type == "audio");
bool isASS = (Trk.codec == "subtitle" && Trk.init.size());
size_t trackIdx = M.trackIDToIndex(tNum, getpid());
bool isVideo = (M.getType(trackIdx) == "video");
bool isAudio = (M.getType(trackIdx) == "audio");
bool isASS = (M.getCodec(trackIdx) == "subtitle" && M.getInit(trackIdx).size());
// If this is a new video keyframe, flush the corresponding trackPredictor
if (isVideo && B.isKeyframe() && bufferedPacks){
@ -523,18 +534,19 @@ namespace Mist{
for (uint64_t frameNo = 0; frameNo < B.getFrameCount(); ++frameNo){
if (frameNo){
if (Trk.codec == "AAC"){
newTime += (1000000 / Trk.rate) / timeScale; // assume ~1000 samples per frame
}else if (Trk.codec == "MP3"){
newTime += (1152000 / Trk.rate) / timeScale; // 1152 samples per frame
}else if (Trk.codec == "DTS"){
if (M.getCodec(trackIdx) == "AAC"){
newTime += (1000000 / M.getRate(trackIdx)) / timeScale; // assume ~1000 samples per frame
}else if (M.getCodec(trackIdx) == "MP3"){
newTime += (1152000 / M.getRate(trackIdx)) / timeScale; // 1152 samples per frame
}else if (M.getCodec(trackIdx) == "DTS"){
// Assume 512 samples per frame (DVD default)
// actual amount can be calculated from data, but data
// is not available during header generation...
// See: http://www.stnsoft.com/DVD/dtshdr.html
newTime += (512000 / Trk.rate) / timeScale;
newTime += (512000 / M.getRate(trackIdx)) / timeScale;
}else{
ERROR_MSG("Unknown frame duration for codec %s - timestamps WILL be wrong!", Trk.codec.c_str());
ERROR_MSG("Unknown frame duration for codec %s - timestamps WILL be wrong!",
M.getCodec(trackIdx).c_str());
}
}
uint32_t frameSize = B.getFrameSize(frameNo);
@ -560,22 +572,26 @@ namespace Mist{
}else{
// We didn't set thisPacket yet. Read another.
// Recursing is fine, this can only happen a few times in a row.
getNext(smart);
getNext(idx);
}
}
void InputEBML::seek(int seekTime){
void InputEBML::seek(uint64_t seekTime, size_t idx){
wantBlocks = true;
clearPredictors();
bufferedPacks = 0;
uint64_t mainTrack = getMainSelectedTrack();
DTSC::Track Trk = myMeta.tracks[mainTrack];
bool isVideo = (Trk.type == "video");
uint64_t seekPos = Trk.keys[0].getBpos();
DTSC::Keys keys(M.keys(mainTrack));
DTSC::Parts parts(M.parts(mainTrack));
uint64_t seekPos = keys.getBpos(0);
// Replay the parts of the previous keyframe, so the timestaps match up
for (unsigned int i = 1; i < Trk.keys.size(); i++){
if (Trk.keys[i].getTime() > seekTime){break;}
seekPos = Trk.keys[i].getBpos();
uint64_t partCount = 0;
for (size_t i = 0; i < keys.getEndValid(); i++){
if (keys.getTime(i) > seekTime){break;}
partCount += keys.getParts(i);
DONTEVEN_MSG("Seeking to %" PRIu64 ", found %" PRIu64 "...", seekTime, keys.getTime(i));
seekPos = keys.getBpos(i);
}
Util::fseek(inFile, seekPos, SEEK_SET);
}

View file

@ -11,14 +11,7 @@ namespace Mist{
uint64_t time, offset, track, dsize, bpos;
bool key;
Util::ResizeablePointer ptr;
packetData(){
time = 0;
offset = 0;
track = 0;
dsize = 0;
bpos = 0;
key = false;
}
packetData() : time(0), offset(0), track(0), dsize(0), bpos(0), key(false){}
void set(uint64_t packTime, uint64_t packOffset, uint64_t packTrack, uint64_t packDataSize,
uint64_t packBytePos, bool isKeyframe, void *dataPtr = 0){
time = packTime;
@ -132,10 +125,12 @@ namespace Mist{
p.offset = ((uint32_t)((frameOffset + (smallestFrame / 2)) / smallestFrame)) * smallestFrame;
}
lastTime = p.time;
INSANE_MSG("Outputting%s %llu+%llu (#%llu, Max=%llu), display at %llu", (p.key ? "KEY" : ""),
p.time, p.offset, rem, maxOffset, p.time + p.offset);
INSANE_MSG("Outputting%s %" PRIu64 "+%" PRIu64 " (#%" PRIu64 ", Max=%" PRIu64
"), display at %" PRIu64,
(p.key ? "KEY" : ""), p.time, p.offset, rem, maxOffset, p.time + p.offset);
return p;
}
void add(uint64_t packTime, uint64_t packOffset, uint64_t packTrack, uint64_t packDataSize,
uint64_t packBytePos, bool isKeyframe, bool isVideo, void *dataPtr = 0){
if (!ctr){lowestTime = packTime;}
@ -155,13 +150,16 @@ namespace Mist{
bool needsLock();
protected:
virtual size_t streamByteCount(){
return totalBytes;
}; // For live streams: to update the stats with correct values.
void fillPacket(packetData &C);
bool checkArguments();
bool preRun();
bool readHeader();
bool readElement();
void getNext(bool smart = true);
void seek(int seekTime);
void getNext(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
void clearPredictors();
FILE *inFile;
Util::ResizeablePointer ptr;
@ -177,6 +175,7 @@ namespace Mist{
bool needHeader(){return needsLock() && !readExistingHeader();}
double timeScale;
bool wantBlocks;
size_t totalBytes;
};
}// namespace Mist

View file

@ -28,6 +28,8 @@ namespace Mist{
capa["codecs"][0u][1u].append("MP3");
}
inputFLV::~inputFLV(){}
bool inputFLV::checkArguments(){
if (config->getString("input") == "-"){
std::cerr << "Input from stdin not yet supported" << std::endl;
@ -77,45 +79,49 @@ namespace Mist{
bool inputFLV::readHeader(){
if (!inFile){return false;}
meta.reInit(config->getString("streamname"));
// Create header file from FLV data
Util::fseek(inFile, 13, SEEK_SET);
AMF::Object amf_storage;
long long int lastBytePos = 13;
uint64_t lastBytePos = 13;
uint64_t bench = Util::getMicros();
while (!feof(inFile) && !FLV::Parse_Error){
if (tmpTag.FileLoader(inFile)){
tmpTag.toMeta(myMeta, amf_storage);
tmpTag.toMeta(meta, amf_storage);
if (!tmpTag.getDataLen()){continue;}
if (tmpTag.needsInitData() && tmpTag.isInitData()){continue;}
myMeta.update(tmpTag.tagTime(), tmpTag.offset(), tmpTag.getTrackID(), tmpTag.getDataLen(),
lastBytePos, tmpTag.isKeyframe);
size_t tNumber = meta.trackIDToIndex(tmpTag.getTrackID(), getpid());
if (tNumber != INVALID_TRACK_ID){
meta.update(tmpTag.tagTime(), tmpTag.offset(), tNumber, tmpTag.getDataLen(), lastBytePos,
tmpTag.isKeyframe);
}
lastBytePos = Util::ftell(inFile);
}
}
bench = Util::getMicros(bench);
INFO_MSG("Header generated in %llu ms: @%lld, %s, %s", bench / 1000, lastBytePos,
myMeta.vod ? "VoD" : "NOVoD", myMeta.live ? "Live" : "NOLive");
INFO_MSG("Header generated in %" PRIu64 " ms: @%" PRIu64 ", %s, %s", bench / 1000, lastBytePos,
M.getVod() ? "VoD" : "NOVoD", M.getLive() ? "Live" : "NOLive");
if (FLV::Parse_Error){
tmpTag = FLV::Tag();
FLV::Parse_Error = false;
ERROR_MSG("Stopping at FLV parse error @%lld: %s", lastBytePos, FLV::Error_Str.c_str());
ERROR_MSG("Stopping at FLV parse error @%" PRIu64 ": %s", lastBytePos, FLV::Error_Str.c_str());
}
myMeta.toFile(config->getString("input") + ".dtsh");
M.toFile(config->getString("input") + ".dtsh");
Util::fseek(inFile, 13, SEEK_SET);
return true;
}
void inputFLV::getNext(bool smart){
long long int lastBytePos = Util::ftell(inFile);
if (selectedTracks.size() == 1){
void inputFLV::getNext(size_t idx){
uint64_t lastBytePos = Util::ftell(inFile);
if (idx != INVALID_TRACK_ID){
uint8_t targetTag = 0x08;
if (selectedTracks.count(1)){targetTag = 0x09;}
if (selectedTracks.count(3)){targetTag = 0x12;}
if (M.getType(idx) == "video"){targetTag = 0x09;}
if (M.getType(idx) == "meta"){targetTag = 0x12;}
FLV::seekToTagType(inFile, targetTag);
}
while (!feof(inFile) && !FLV::Parse_Error){
if (tmpTag.FileLoader(inFile)){
if (!selectedTracks.count(tmpTag.getTrackID())){
if (idx != INVALID_TRACK_ID && M.getID(idx) != tmpTag.getTrackID()){
lastBytePos = Util::ftell(inFile);
continue;
}
@ -129,22 +135,22 @@ namespace Mist{
if (FLV::Parse_Error){
FLV::Parse_Error = false;
tmpTag = FLV::Tag();
FAIL_MSG("FLV error @ %lld: %s", lastBytePos, FLV::Error_Str.c_str());
FAIL_MSG("FLV error @ %" PRIu64 ": %s", lastBytePos, FLV::Error_Str.c_str());
thisPacket.null();
return;
}
if (!tmpTag.getDataLen() || (tmpTag.needsInitData() && tmpTag.isInitData())){
return getNext();
return getNext(idx);
}
thisPacket.genericFill(tmpTag.tagTime(), tmpTag.offset(), tmpTag.getTrackID(), tmpTag.getData(),
tmpTag.getDataLen(), lastBytePos, tmpTag.isKeyframe); // init packet from tmpTags data
size_t tNumber = meta.trackIDToIndex(tmpTag.getTrackID(), getpid());
thisPacket.genericFill(tmpTag.tagTime(), tmpTag.offset(), tNumber, tmpTag.getData(),
tmpTag.getDataLen(), lastBytePos, tmpTag.isKeyframe);
DTSC::Track &trk = myMeta.tracks[tmpTag.getTrackID()];
if (trk.codec == "PCM" && trk.size == 16){
if (M.getCodec(idx) == "PCM" && M.getSize(idx) == 16){
char *ptr = 0;
size_t ptrSize = 0;
thisPacket.getString("data", ptr, ptrSize);
for (uint32_t i = 0; i < ptrSize; i += 2){
for (size_t i = 0; i < ptrSize; i += 2){
char tmpchar = ptr[i];
ptr[i] = ptr[i + 1];
ptr[i + 1] = tmpchar;
@ -152,29 +158,12 @@ namespace Mist{
}
}
void inputFLV::seek(int seekTime){
void inputFLV::seek(uint64_t seekTime, size_t idx){
// We will seek to the corresponding keyframe of the video track if selected, otherwise audio
// keyframe. Flv files are never multi-track, so track 1 is video, track 2 is audio.
int trackSeek = (selectedTracks.count(1) ? 1 : 2);
uint64_t seekPos = myMeta.tracks[trackSeek].keys[0].getBpos();
for (unsigned int i = 0; i < myMeta.tracks[trackSeek].keys.size(); i++){
if (myMeta.tracks[trackSeek].keys[i].getTime() > seekTime){break;}
seekPos = myMeta.tracks[trackSeek].keys[i].getBpos();
}
Util::fseek(inFile, seekPos, SEEK_SET);
}
void inputFLV::trackSelect(std::string trackSpec){
selectedTracks.clear();
size_t index;
while (trackSpec != ""){
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
if (index != std::string::npos){
trackSpec.erase(0, index + 1);
}else{
trackSpec = "";
}
}
size_t seekTrack = (idx == INVALID_TRACK_ID ? M.mainTrack() : idx);
DTSC::Keys keys(M.keys(seekTrack));
uint32_t keyNum = keys.getNumForTime(seekTime);
Util::fseek(inFile, keys.getBpos(keyNum), SEEK_SET);
}
}// namespace Mist

View file

@ -6,15 +6,15 @@ namespace Mist{
class inputFLV : public Input{
public:
inputFLV(Util::Config *cfg);
~inputFLV();
protected:
// Private Functions
bool checkArguments();
bool preRun();
bool readHeader();
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
void getNext(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
bool keepRunning();
FLV::Tag tmpTag;
uint64_t lastModTime;

View file

@ -11,6 +11,8 @@ namespace Mist{
bool checkArguments(){return false;};
bool readHeader(){return false;};
bool needHeader(){return false;};
void getNext(size_t idx = INVALID_TRACK_ID){}
void seek(uint64_t time, size_t idx = INVALID_TRACK_ID){}
};
}// namespace Mist

View file

@ -17,10 +17,9 @@ namespace Mist{
inputProcess = 0;
}
bool InputH264::preRun(){
bool InputH264::openStreamSource(){
if (config->getString("input") != "-"){
std::string input = config->getString("input");
const char *argv[2];
input = input.substr(10);
char *args[128];
@ -50,15 +49,20 @@ namespace Mist{
myConn.open(fileno(stdout), fileno(stdin));
}
myConn.Received().splitter.assign("\000\000\001", 3);
myMeta.vod = false;
myMeta.live = true;
myMeta.tracks[1].type = "video";
myMeta.tracks[1].codec = "H264";
myMeta.tracks[1].trackID = 1;
waitsSinceData = 0;
return true;
}
void InputH264::parseStreamHeader(){
tNumber = meta.addTrack();
meta.setType(tNumber, "video");
meta.setCodec(tNumber, "H264");
meta.setID(tNumber, tNumber);
waitsSinceData = 0;
INFO_MSG("Waiting for init data...");
while (myConn && !M.getInit(tNumber).size()){getNext();}
INFO_MSG("Init data received!");
}
bool InputH264::checkArguments(){
std::string input = config->getString("input");
if (input != "-" && input.substr(0, 10) != "h264-exec:"){
@ -68,7 +72,7 @@ namespace Mist{
return true;
}
void InputH264::getNext(bool smart){
void InputH264::getNext(size_t idx){
do{
if (!myConn.spool()){
Util::sleep(25);
@ -87,18 +91,18 @@ namespace Mist{
while (nalSize && NAL.data()[nalSize - 1] == 0){--nalSize;}
if (!nalSize){continue;}
uint8_t nalType = NAL.data()[0] & 0x1F;
INSANE_MSG("NAL unit, type %u, size %lu", nalType, nalSize);
INSANE_MSG("NAL unit, type %u, size %" PRIu32, nalType, nalSize);
if (nalType == 7 || nalType == 8){
if (nalType == 7){spsInfo = NAL.substr(0, nalSize);}
if (nalType == 8){ppsInfo = NAL.substr(0, nalSize);}
if (!myMeta.tracks[1].init.size() && spsInfo.size() && ppsInfo.size()){
if (!meta.getInit(tNumber).size() && spsInfo.size() && ppsInfo.size()){
h264::sequenceParameterSet sps(spsInfo.data(), spsInfo.size());
h264::SPSMeta spsChar = sps.getCharacteristics();
myMeta.tracks[1].width = spsChar.width;
myMeta.tracks[1].height = spsChar.height;
myMeta.tracks[1].fpks = spsChar.fps * 1000;
if (myMeta.tracks[1].fpks < 100 || myMeta.tracks[1].fpks > 1000000){
myMeta.tracks[1].fpks = 0;
meta.setWidth(tNumber, spsChar.width);
meta.setHeight(tNumber, spsChar.height);
meta.setFpks(tNumber, spsChar.fps * 1000);
if (M.getFpks(tNumber) < 100 || M.getFpks(tNumber) > 1000000){
meta.setFpks(tNumber, 0);
}
MP4::AVCC avccBox;
avccBox.setVersion(1);
@ -109,14 +113,14 @@ namespace Mist{
avccBox.setSPS(spsInfo);
avccBox.setPPSCount(1);
avccBox.setPPS(ppsInfo);
myMeta.tracks[1].init = std::string(avccBox.payload(), avccBox.payloadSize());
meta.setInit(tNumber, avccBox.payload(), avccBox.payloadSize());
}
continue;
}
if (myMeta.tracks[1].init.size()){
if (M.getInit(tNumber).size()){
uint64_t ts = Util::bootMS() - startTime;
if (myMeta.tracks[1].fpks){ts = frameCount * (1000000 / myMeta.tracks[1].fpks);}
thisPacket.genericFill(ts, 0, 1, 0, 0, 0, h264::isKeyframe(NAL.data(), nalSize));
if (M.getFpks(tNumber)){ts = frameCount * (1000000 / M.getFpks(tNumber));}
thisPacket.genericFill(ts, 0, tNumber, 0, 0, 0, h264::isKeyframe(NAL.data(), nalSize));
thisPacket.appendNal(NAL.data(), nalSize);
++frameCount;
return;

View file

@ -8,24 +8,24 @@ namespace Mist{
InputH264(Util::Config *cfg);
protected:
virtual bool needHeader(){return false;}
bool checkArguments();
bool preRun();
void getNext(bool smart = true);
void getNext(size_t idx = INVALID_TRACK_ID);
Socket::Connection myConn;
std::string ppsInfo;
std::string spsInfo;
uint64_t frameCount;
// Empty defaults
bool readHeader(){return true;}
bool openStreamSource(){return true;}
bool openStreamSource();
void closeStreamSource(){}
void parseStreamHeader(){}
void seek(int seekTime){}
void trackSelect(std::string trackSpec){}
void parseStreamHeader();
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID){}
bool needsLock(){return false;}
uint64_t startTime;
pid_t inputProcess;
uint32_t waitsSinceData;
size_t tNumber;
};
}// namespace Mist

View file

@ -118,7 +118,7 @@ namespace Mist{
/// Called by the global callbackFunc, to prevent timeouts
bool inputHLS::callback(){
if (nProxy.userClient.isAlive()){nProxy.userClient.keepAlive();}
keepAlive();
return config->is_active;
}
@ -415,7 +415,7 @@ namespace Mist{
if (key == "TARGETDURATION"){
waitTime = atoi(val.c_str()) / 2;
if (waitTime < 5){waitTime = 5;}
if (waitTime < 2){waitTime = 2;}
}
if (key == "MEDIA-SEQUENCE"){fileNo = atoll(val.c_str());}
@ -525,7 +525,8 @@ namespace Mist{
std::string test = root.link(entry.filename).getFilePath();
fileSource.open(test.c_str(), std::ios::ate | std::ios::binary);
if (!fileSource.good()){WARN_MSG("file: %s, error: %s", test.c_str(), strerror(errno));}
totalBytes += fileSource.tellg();
entry.byteEnd = fileSource.tellg();
totalBytes += entry.byteEnd;
}
entry.timestamp = lastTimestamp + startTime;
@ -588,20 +589,6 @@ namespace Mist{
return true;
}
void inputHLS::trackSelect(std::string trackSpec){
selectedTracks.clear();
size_t index;
while (trackSpec != ""){
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
if (index != std::string::npos){
trackSpec.erase(0, index + 1);
}else{
trackSpec = "";
}
}
}
void inputHLS::parseStreamHeader(){
if (!initPlaylist(config->getString("input"))){
FAIL_MSG("Failed to load HLS playlist, aborting");
@ -668,6 +655,8 @@ namespace Mist{
}while (!segDowner.atEnd());
if (preCounter < counter){break;}// We're done reading this playlist!
}
in.close();
}
tsStream.clear();
currentPlaylist = 0;
@ -681,13 +670,12 @@ namespace Mist{
bool hasHeader = false;
// See whether a separate header file exists.
DTSC::File tmp(config->getString("input") + ".dtsh");
if (tmp){
myMeta = tmp.getMeta();
if (myMeta){hasHeader = true;}
}
meta.reInit(config->getString("streamname"), config->getString("input") + ".dtsh");
hasHeader = (bool)M;
if (!hasHeader){myMeta = DTSC::Meta();}
if (M){return true;}
if (!hasHeader){meta.reInit(config->getString("streamname"), true);}
TS::Packet packet; // to analyse and extract data
@ -728,18 +716,21 @@ namespace Mist{
counter++;
}
if (!hasHeader && (!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())){
tsStream.initializeMetadata(myMeta, tmpTrackId, packetId);
size_t idx = M.trackIDToIndex(packetId, getpid());
INFO_MSG("PacketID: %" PRIu64 ", pid: %d, mapped to %zu", packetId, getpid(), idx);
if (!hasHeader && (idx == INVALID_TRACK_ID || !M.getCodec(idx).size())){
tsStream.initializeMetadata(meta, tmpTrackId, packetId);
INFO_MSG("InitializingMeta for track %zu -> %zu", tmpTrackId, packetId);
idx = M.trackIDToIndex(packetId, getpid());
}
if (!hasHeader){
headerPack.getString("data", data, dataLen);
uint64_t pBPos = headerPack.getInt("bpos");
// keyframe data exists, so always add 19 bytes keyframedata.
long long packOffset = headerPack.hasMember("offset") ? headerPack.getInt("offset") : 0;
long long packSendSize = 24 + (packOffset ? 17 : 0) + (entId >= 0 ? 15 : 0) + 19 + dataLen + 11;
myMeta.update(headerPack.getTime(), packOffset, packetId, dataLen, entId,
uint32_t packOffset = headerPack.hasMember("offset") ? headerPack.getInt("offset") : 0;
size_t packSendSize = 24 + (packOffset ? 17 : 0) + (entId >= 0 ? 15 : 0) + 19 + dataLen + 11;
meta.update(headerPack.getTime(), packOffset, idx, dataLen, entId,
headerPack.hasMember("keyframe"), packSendSize);
}
}
@ -766,18 +757,17 @@ namespace Mist{
counter++;
}
if (!hasHeader && (!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())){
tsStream.initializeMetadata(myMeta, tmpTrackId, packetId);
if (!hasHeader && (idx == INVALID_TRACK_ID || !M.getCodec(idx).size())){
tsStream.initializeMetadata(meta, tmpTrackId, packetId);
idx = M.trackIDToIndex(packetId, getpid());
}
if (!hasHeader){
headerPack.getString("data", data, dataLen);
uint64_t pBPos = headerPack.getInt("bpos");
// keyframe data exists, so always add 19 bytes keyframedata.
long long packOffset = headerPack.hasMember("offset") ? headerPack.getInt("offset") : 0;
long long packSendSize = 24 + (packOffset ? 17 : 0) + (entId >= 0 ? 15 : 0) + 19 + dataLen + 11;
myMeta.update(headerPack.getTime(), packOffset, packetId, dataLen, entId,
meta.update(headerPack.getTime(), packOffset, idx, dataLen, entId,
headerPack.hasMember("keyframe"), packSendSize);
}
tsStream.getEarliestPacket(headerPack);
@ -790,10 +780,8 @@ namespace Mist{
if (streamIsLive){return true;}
INFO_MSG("write header file...");
std::ofstream oFile((config->getString("input") + ".dtsh").c_str());
oFile << myMeta.toJSON().toNetPacked();
oFile.close();
M.toFile((config->getString("input") + ".dtsh").c_str());
in.close();
return true;
}
@ -802,34 +790,30 @@ namespace Mist{
bool inputHLS::openStreamSource(){return true;}
void inputHLS::getNext(bool smart){
void inputHLS::getNext(size_t idx){
INSANE_MSG("Getting next");
uint32_t tid = 0;
bool finished = false;
if (selectedTracks.size()){tid = *selectedTracks.begin();}
if (userSelect.size()){tid = userSelect.begin()->first;}
thisPacket.null();
while (config->is_active && (needsLock() || nProxy.userClient.isAlive())){
while (config->is_active && (needsLock() || keepAlive())){
// Check if we have a packet
bool hasPacket = false;
if (streamIsLive){
hasPacket = tsStream.hasPacketOnEachTrack() || (segDowner.atEnd() && tsStream.hasPacket());
}else{
hasPacket = tsStream.hasPacket(getMappedTrackId(tid));
hasPacket = tsStream.hasPacket(M.getID(idx) & 0xFFFF);
}
// Yes? Excellent! Read and return it.
if (hasPacket){
// Read
if (myMeta.live){
if (M.getLive()){
tsStream.getEarliestPacket(thisPacket);
tid = getOriginalTrackId(currentPlaylist, thisPacket.getTrackId());
if (!tid){
INFO_MSG("Track %" PRIu64 " on PLS %u -> %" PRIu32, thisPacket.getTrackId(), currentPlaylist, tid);
continue;
}
tid = M.trackIDToIndex((((uint64_t)currentPlaylist) << 16) + thisPacket.getTrackId(), getpid());
}else{
tsStream.getPacket(getMappedTrackId(tid), thisPacket);
tsStream.getPacket(M.getID(idx) & 0xFFFF, thisPacket);
}
if (!thisPacket){
FAIL_MSG("Could not getNext TS packet!");
@ -940,25 +924,19 @@ namespace Mist{
}
// Note: bpos is overloaded here for playlist entry!
void inputHLS::seek(int seekTime){
void inputHLS::seek(uint64_t seekTime, size_t idx){
plsTimeOffset.clear();
plsLastTime.clear();
plsInterval.clear();
tsStream.clear();
int trackId = 0;
uint64_t trackId = M.getID(idx);
unsigned long plistEntry = 0xFFFFFFFFull;
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
unsigned long thisBPos = 0;
for (std::deque<DTSC::Key>::iterator keyIt = myMeta.tracks[*it].keys.begin();
keyIt != myMeta.tracks[*it].keys.end(); keyIt++){
if (keyIt->getTime() > seekTime){break;}
thisBPos = keyIt->getBpos();
}
if (thisBPos < plistEntry){
plistEntry = thisBPos;
trackId = *it;
}
unsigned long plistEntry = 0;
DTSC::Keys keys(M.keys(idx));
for (size_t i = keys.getFirstValid(); i < keys.getEndValid(); i++){
if (keys.getTime(i) > seekTime){break;}
plistEntry = keys.getBpos(i);
}
if (plistEntry < 1){
@ -995,7 +973,7 @@ namespace Mist{
}
}
int inputHLS::getEntryId(int playlistId, uint64_t bytePos){
size_t inputHLS::getEntryId(uint32_t playlistId, uint64_t bytePos){
if (bytePos == 0){return 0;}
tthread::lock_guard<tthread::mutex> guard(entryMutex);
for (int i = 0; i < listEntries[playlistId].size(); i++){
@ -1248,9 +1226,9 @@ namespace Mist{
/// return the playlist id from which we need to read the first upcoming segment
/// by timestamp.
/// this will keep the playlists in sync while reading segments.
int inputHLS::firstSegment(){
size_t inputHLS::firstSegment(){
// Only one selected? Immediately return the right playlist.
if (selectedTracks.size() == 1){return getMappedTrackPlaylist(*selectedTracks.begin());}
if (userSelect.size() == 1){return ((M.getID(userSelect.begin()->first) >> 16) & 0xFFFF);}
uint64_t firstTimeStamp = 0;
int tmpId = -1;
int segCount = 0;

View file

@ -27,8 +27,8 @@ namespace Mist{
uint64_t bytePos;
uint64_t mUTC; ///< UTC unix millis timestamp of first packet, if known
float duration;
unsigned int timestamp;
unsigned int wait;
uint64_t timestamp;
uint64_t wait;
char ivec[16];
char keyAES[16];
};
@ -75,10 +75,10 @@ namespace Mist{
int noChangeCount;
uint64_t lastFileIndex;
int waitTime;
uint64_t waitTime;
PlaylistType playlistType;
unsigned int lastTimestamp;
unsigned int startTime;
uint64_t lastTimestamp;
uint64_t startTime;
uint64_t nextUTC; ///< If non-zero, the UTC timestamp of the next segment on this playlist
char keyAES[16];
std::map<std::string, std::string> keys;
@ -103,7 +103,7 @@ namespace Mist{
int version;
int targetDuration;
bool endPlaylist;
int currentPlaylist;
uint64_t currentPlaylist;
bool allowRemap; ///< True if the next packet may remap the timestamps
bool allowSoftRemap; ///< True if the next packet may soft-remap the timestamps
@ -113,7 +113,7 @@ namespace Mist{
std::map<int, uint64_t> plsLastTime;
std::map<int, uint64_t> plsInterval;
int currentIndex;
size_t currentIndex;
std::string currentFile;
TS::Stream tsStream; ///< Used for parsing the incoming ts stream
@ -128,9 +128,9 @@ namespace Mist{
bool preSetup();
bool readHeader();
bool needHeader(){return true;}
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
void getNext(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
FILE *inFile;
FILE *tsFile;
@ -141,10 +141,7 @@ namespace Mist{
void parseStreamHeader();
uint32_t getMappedTrackId(uint64_t id);
uint32_t getMappedTrackPlaylist(uint64_t id);
uint64_t getOriginalTrackId(uint32_t playlistId, uint32_t id);
int getEntryId(int playlistId, uint64_t bytePos);
size_t getEntryId(uint32_t playlistId, uint64_t bytePos);
};
}// namespace Mist

Some files were not shown because too many files have changed in this diff Show more