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

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,37 +138,40 @@ 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"){
// Command message
std::string toRec = srcConn.Received().copy(8);
unsigned long rSize = Bit::btohl(toRec.c_str() + 4);
if (!srcConn.Received().available(8 + rSize)){
nProxy.userClient.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{
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);
}
break;
}
}else{
INFO_MSG("Received a wrong type of packet - '%s'", srcConn.Received().copy(4).c_str());
break;
}
}else{
if (!srcConn.Received().available(8)){
Util::sleep(100);
nProxy.userClient.keepAlive();
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);
uint32_t rSize = Bit::btohl(toRec.c_str() + 4);
if (!srcConn.Received().available(8 + rSize)){
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);
continue;
}
std::string dataPacket = srcConn.Received().remove(8 + rSize);
DTSC::Packet metaPack(dataPacket.data(), dataPacket.size());
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;
}
}
@ -194,25 +206,26 @@ namespace Mist{
void inputDTSC::closeStreamSource(){srcConn.close();}
bool inputDTSC::checkArguments(){
if (!needsLock()){
return true;
}else{
if (!config->getString("streamname").size()){
if (config->getString("output") == "-"){
std::cerr << "Output to stdout not yet supported" << std::endl;
return false;
}
}else{
if (config->getString("output") != "-"){
std::cerr << "File output in player mode not supported" << std::endl;
return false;
}
if (!needsLock()){return true;}
if (!config->getString("streamname").size()){
if (config->getString("output") == "-"){
std::cerr << "Output to stdout not yet supported" << std::endl;
return false;
}
}else{
if (config->getString("output") != "-"){
std::cerr << "File output in player mode not supported" << std::endl;
return false;
}
// open File
inFile = DTSC::File(config->getString("input"));
if (!inFile){return false;}
}
// open File
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");
return false;
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;
}
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);
}
free(pkt);
}while (moreHeader);
}
myMeta = DTSC::Meta(inFile.getMeta());
DEBUG_MSG(DLVL_DEVEL, "Meta read in with %lu tracks", myMeta.tracks.size());
return true;
return meta;
}
void inputDTSC::getNext(bool smart){
void inputDTSC::getNext(size_t idx){
if (!needsLock()){
thisPacket.reInit(srcConn);
while (config->is_active){
if (thisPacket.getVersion() == DTSC::DTCM){
nProxy.userClient.keepAlive();
std::string cmd;
thisPacket.getString("cmd", cmd);
if (cmd == "reset"){
// Read next packet
thisPacket.reInit(srcConn);
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);
}
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());
}
}
return; // we have a packet
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){
// userClient.keepAlive();
std::string cmd;
thisPacket.getString("cmd", cmd);
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 nM("", thisPacket.getScan());
meta.merge(nM, false, false);
thisPacket.reInit(srcConn); // read the next packet before continuing
continue; // parse the next packet before returning
}
thisPacket = DTSC::Packet(thisPacket, M.trackIDToIndex(thisPacket.getTrackId(), getpid()));
return; // we have a packet
}
}
void inputDTSC::seek(uint64_t seekTime, size_t idx){
currentPositions.clear();
if (idx != INVALID_TRACK_ID){
seekNext(seekTime, idx, true);
}else{
if (smart){
inFile.seekNext();
}else{
inFile.parseNext();
std::set<size_t> tracks = M.getValidTracks();
for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){
seekNext(seekTime, *it, true);
}
thisPacket = inFile.getPacket();
}
}
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{
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{
trackSpec = "";
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());}
@ -520,12 +520,13 @@ namespace Mist{
memset(entry.keyAES, 0, 16);
}
if (!isUrl()){
std::ifstream fileSource;
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();
if (!isUrl()){
std::ifstream fileSource;
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));}
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,19 +716,22 @@ 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,
headerPack.hasMember("keyframe"), packSendSize);
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,19 +757,18 @@ 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,
headerPack.hasMember("keyframe"), packSendSize);
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

View file

@ -40,297 +40,210 @@ namespace Mist{
}
return true;
}
bool inputISMV::preRun(){
// open File
inFile = fopen(config->getString("input").c_str(), "r");
if (!inFile){return false;}
return true;
return inFile; // True if not null
}
bool inputISMV::readHeader(){
if (!inFile){return false;}
meta.reInit(streamName);
// parse ismv header
fseek(inFile, 0, SEEK_SET);
std::string ftyp;
readBox("ftyp", ftyp);
if (ftyp == ""){return false;}
std::string boxRes;
readBox("moov", boxRes);
if (boxRes == ""){return false;}
MP4::MOOV hdrBox;
hdrBox.read(boxRes);
parseMoov(hdrBox);
int tId;
std::vector<MP4::trunSampleInformation> trunSamples;
std::vector<std::string> initVecs;
std::string mdat;
unsigned int currOffset;
JSON::Value lastPack;
unsigned int lastBytePos = 0;
std::map<int, unsigned int> currentDuration;
unsigned int curBytePos = ftell(inFile);
// Skip mandatory ftyp box
MP4::skipBox(inFile);
MP4::MOOV moovBox;
moovBox.read(inFile);
parseMoov(moovBox);
std::map<size_t, uint64_t> duration;
uint64_t currOffset;
uint64_t lastBytePos = 0;
uint64_t curBytePos = ftell(inFile);
// parse fragments form here
while (parseFrag(tId, trunSamples, initVecs, mdat)){
if (!currentDuration.count(tId)){currentDuration[tId] = 0;}
size_t tId;
std::vector<MP4::trunSampleInformation> trunSamples;
while (readMoofSkipMdat(tId, trunSamples) && !feof(inFile)){
if (!duration.count(tId)){duration[tId] = 0;}
currOffset = 8;
int i = 0;
while (currOffset < mdat.size()){
lastPack.null();
lastPack["time"] = currentDuration[tId] / 10000;
lastPack["trackid"] = tId;
lastPack["data"] = mdat.substr(currOffset, trunSamples[i].sampleSize);
if (initVecs.size() == trunSamples.size()){lastPack["ivec"] = initVecs[i];}
lastPack["duration"] = trunSamples[i].sampleDuration;
if (myMeta.tracks[tId].type == "video"){
if (i){
lastBytePos++;
}else{
lastPack["keyframe"] = 1;
lastBytePos = curBytePos;
}
lastPack["bpos"] = lastBytePos;
unsigned int offsetConv = trunSamples[i].sampleOffset / 10000;
lastPack["offset"] = (int)offsetConv;
for (std::vector<MP4::trunSampleInformation>::iterator it = trunSamples.begin();
it != trunSamples.end(); it++){
bool first = (it == trunSamples.begin());
int64_t offsetConv = 0;
if (M.getType(tId) == "video"){offsetConv = it->sampleOffset / 10000;}
if (first){
lastBytePos = curBytePos;
}else{
if (i == 0){
lastPack["keyframe"] = 1;
lastPack["bpos"] = curBytePos;
}
++lastBytePos;
}
myMeta.update(lastPack);
currentDuration[tId] += trunSamples[i].sampleDuration;
currOffset += trunSamples[i].sampleSize;
i++;
meta.update(duration[tId] / 10000, offsetConv, tId, it->sampleSize, lastBytePos, first);
duration[tId] += it->sampleDuration;
currOffset += it->sampleSize;
}
curBytePos = ftell(inFile);
}
myMeta.toFile(config->getString("input") + ".dtsh");
M.toFile(config->getString("input") + ".dtsh");
return true;
}
void inputISMV::getNext(bool smart){
static JSON::Value thisPack;
thisPack.null();
if (!buffered.size()){
thisPacket.null();
return;
}
int tId = buffered.begin()->trackId;
thisPack["time"] = (uint64_t)(buffered.begin()->time / 10000);
thisPack["trackid"] = tId;
fseek(inFile, buffered.begin()->position, SEEK_SET);
char *tmpData = (char *)malloc(buffered.begin()->size * sizeof(char));
fread(tmpData, buffered.begin()->size, 1, inFile);
thisPack["data"] = std::string(tmpData, buffered.begin()->size);
free(tmpData);
if (buffered.begin()->iVec != ""){thisPack["ivec"] = buffered.begin()->iVec;}
if (myMeta.tracks[tId].type == "video"){
if (buffered.begin()->isKeyFrame){thisPack["keyframe"] = 1;}
thisPack["offset"] = (uint64_t)(buffered.begin()->offset / 10000);
}else{
if (buffered.begin()->isKeyFrame){thisPack["keyframe"] = 1;}
}
thisPack["bpos"] = (uint64_t)buffered.begin()->position;
void inputISMV::getNext(size_t idx){
thisPacket.null();
if (!buffered.size()){return;}
seekPos thisPos = *buffered.begin();
buffered.erase(buffered.begin());
if (buffered.size() < 2 * selectedTracks.size()){
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
parseFragHeader(*it, lastKeyNum[*it]);
lastKeyNum[*it]++;
fseek(inFile, thisPos.position, SEEK_SET);
dataPointer.allocate(thisPos.size);
fread(dataPointer, thisPos.size, 1, inFile);
thisPacket.genericFill(thisPos.time / 10000, thisPos.offset / 10000, thisPos.trackId,
dataPointer, thisPos.size, 0, thisPos.isKeyFrame);
if (buffered.size() < 2 * (idx == INVALID_TRACK_ID ? M.getValidTracks().size() : 1)){
std::set<size_t> validTracks = M.getValidTracks();
if (idx != INVALID_TRACK_ID){
validTracks.clear();
validTracks.insert(idx);
}
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
bufferFragmentData(*it, ++lastKeyNum[*it]);
}
}
std::string tmpStr = thisPack.toNetPacked();
thisPacket.reInit(tmpStr.data(), tmpStr.size());
if (idx != INVALID_TRACK_ID && thisPacket.getTrackId() != M.getID(idx)){getNext(idx);}
}
///\brief Overloads Input::atKeyFrame, for ISMV always sets the keyframe number
bool inputISMV::atKeyFrame(){return thisPacket.getFlag("keyframe");}
void inputISMV::seek(int seekTime){
void inputISMV::seek(uint64_t seekTime, size_t idx){
buffered.clear();
// Seek to corresponding keyframes on EACH track
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
unsigned int i;
for (i = 0; i < myMeta.tracks[*it].keys.size(); i++){
if (myMeta.tracks[*it].keys[i].getTime() > seekTime && i > 0){// Ehh, whut?
break;
}
}
i--;
DEBUG_MSG(DLVL_DEVEL, "ISMV seek frag %d:%d", *it, i);
parseFragHeader(*it, i);
lastKeyNum[*it] = i + 1;
}
}
lastKeyNum.clear();
void inputISMV::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 = "";
}
// Select tracks
std::set<size_t> validTracks = M.getValidTracks();
if (idx != INVALID_TRACK_ID){
validTracks.clear();
validTracks.insert(idx);
}
// For each selected track
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); ++it){
DTSC::Keys keys(M.keys(*it));
uint32_t i;
for (i = keys.getFirstValid(); i < keys.getEndValid(); i++){
if (keys.getTime(i) >= seekTime){break;}
}
INFO_MSG("ISMV seek frag %zu:%" PRIu32, *it, i);
bufferFragmentData(*it, i);
lastKeyNum[*it] = i;
}
seek(0);
}
void inputISMV::parseMoov(MP4::MOOV &moovBox){
for (unsigned int i = 0; i < moovBox.getContentCount(); i++){
if (moovBox.getContent(i).isType("mvhd")){
MP4::MVHD content = (MP4::MVHD &)moovBox.getContent(i);
}
if (moovBox.getContent(i).isType("trak")){
MP4::TRAK content = (MP4::TRAK &)moovBox.getContent(i);
int trackId;
for (unsigned int j = 0; j < content.getContentCount(); j++){
if (content.getContent(j).isType("tkhd")){
MP4::TKHD subContent = (MP4::TKHD &)content.getContent(j);
trackId = subContent.getTrackID();
myMeta.tracks[trackId].trackID = trackId;
}
if (content.getContent(j).isType("mdia")){
MP4::MDIA subContent = (MP4::MDIA &)content.getContent(j);
for (unsigned int k = 0; k < subContent.getContentCount(); k++){
if (subContent.getContent(k).isType("hdlr")){
MP4::HDLR subsubContent = (MP4::HDLR &)subContent.getContent(k);
if (subsubContent.getHandlerType() == "soun"){
myMeta.tracks[trackId].type = "audio";
}
if (subsubContent.getHandlerType() == "vide"){
myMeta.tracks[trackId].type = "video";
}
}
if (subContent.getContent(k).isType("minf")){
MP4::MINF subsubContent = (MP4::MINF &)subContent.getContent(k);
for (unsigned int l = 0; l < subsubContent.getContentCount(); l++){
if (subsubContent.getContent(l).isType("stbl")){
MP4::STBL stblBox = (MP4::STBL &)subsubContent.getContent(l);
for (unsigned int m = 0; m < stblBox.getContentCount(); m++){
if (stblBox.getContent(m).isType("stsd")){
MP4::STSD stsdBox = (MP4::STSD &)stblBox.getContent(m);
for (unsigned int n = 0; n < stsdBox.getEntryCount(); n++){
if (stsdBox.getEntry(n).isType("mp4a") ||
stsdBox.getEntry(n).isType("enca")){
MP4::MP4A mp4aBox = (MP4::MP4A &)stsdBox.getEntry(n);
myMeta.tracks[trackId].codec = "AAC";
std::string tmpStr;
tmpStr += (char)((mp4aBox.toAACInit() & 0xFF00) >> 8);
tmpStr += (char)(mp4aBox.toAACInit() & 0x00FF);
myMeta.tracks[trackId].init = tmpStr;
myMeta.tracks[trackId].channels = mp4aBox.getChannelCount();
myMeta.tracks[trackId].size = mp4aBox.getSampleSize();
myMeta.tracks[trackId].rate = mp4aBox.getSampleRate();
}
if (stsdBox.getEntry(n).isType("avc1") ||
stsdBox.getEntry(n).isType("encv")){
MP4::AVC1 avc1Box = (MP4::AVC1 &)stsdBox.getEntry(n);
myMeta.tracks[trackId].height = avc1Box.getHeight();
myMeta.tracks[trackId].width = avc1Box.getWidth();
myMeta.tracks[trackId].init =
std::string(avc1Box.getCLAP().payload(), avc1Box.getCLAP().payloadSize());
myMeta.tracks[trackId].codec = "H264";
}
}
}
}
}
}
}
}
}
std::deque<MP4::TRAK> trak = moovBox.getChildren<MP4::TRAK>();
for (std::deque<MP4::TRAK>::iterator it = trak.begin(); it != trak.end(); it++){
size_t tNumber = meta.addTrack();
meta.setID(tNumber, it->getChild<MP4::TKHD>().getTrackID());
MP4::MDIA mdia = it->getChild<MP4::MDIA>();
MP4::HDLR hdlr = mdia.getChild<MP4::HDLR>();
if (hdlr.getHandlerType() == "soun"){meta.setType(tNumber, "audio");}
if (hdlr.getHandlerType() == "vide"){meta.setType(tNumber, "video");}
MP4::STSD stsd = mdia.getChild<MP4::MINF>().getChild<MP4::STBL>().getChild<MP4::STSD>();
for (size_t i = 0; i < stsd.getEntryCount(); ++i){
if (stsd.getEntry(i).isType("mp4a") || stsd.getEntry(i).isType("enca")){
MP4::MP4A mp4aBox = (MP4::MP4A &)stsd.getEntry(i);
std::string tmpStr;
tmpStr += (char)((mp4aBox.toAACInit() & 0xFF00) >> 8);
tmpStr += (char)(mp4aBox.toAACInit() & 0x00FF);
meta.setCodec(tNumber, "AAC");
meta.setInit(tNumber, tmpStr);
meta.setChannels(tNumber, mp4aBox.getChannelCount());
meta.setSize(tNumber, mp4aBox.getSampleSize());
meta.setRate(tNumber, mp4aBox.getSampleRate());
}
if (stsd.getEntry(i).isType("avc1") || stsd.getEntry(i).isType("encv")){
MP4::AVC1 avc1Box = (MP4::AVC1 &)stsd.getEntry(i);
meta.setCodec(tNumber, "H264");
meta.setInit(tNumber, avc1Box.getCLAP().payload(), avc1Box.getCLAP().payloadSize());
meta.setHeight(tNumber, avc1Box.getHeight());
meta.setWidth(tNumber, avc1Box.getWidth());
}
}
}
}
bool inputISMV::parseFrag(int &tId, std::vector<MP4::trunSampleInformation> &trunSamples,
std::vector<std::string> &initVecs, std::string &mdat){
tId = -1;
bool inputISMV::readMoofSkipMdat(size_t &tId, std::vector<MP4::trunSampleInformation> &trunSamples){
tId = INVALID_TRACK_ID;
trunSamples.clear();
initVecs.clear();
mdat.clear();
std::string boxRes;
readBox("moof", boxRes);
if (boxRes == ""){return false;}
MP4::MOOF moof;
moof.read(boxRes);
for (unsigned int i = 0; i < moof.getContentCount(); i++){
if (moof.getContent(i).isType("traf")){
MP4::TRAF trafBox = (MP4::TRAF &)moof.getContent(i);
for (unsigned int j = 0; j < trafBox.getContentCount(); j++){
if (trafBox.getContent(j).isType("trun")){
MP4::TRUN trunBox = (MP4::TRUN &)trafBox.getContent(j);
for (unsigned int i = 0; i < trunBox.getSampleInformationCount(); i++){
trunSamples.push_back(trunBox.getSampleInformation(i));
}
}
if (trafBox.getContent(j).isType("tfhd")){
tId = ((MP4::TFHD &)trafBox.getContent(j)).getTrackID();
}
/*LTS-START*/
if (trafBox.getContent(j).isType("uuid")){
if (((MP4::UUID &)trafBox.getContent(j)).getUUID() ==
"a2394f52-5a9b-4f14-a244-6c427c648df4"){
MP4::UUID_SampleEncryption uuidBox = (MP4::UUID_SampleEncryption &)trafBox.getContent(j);
for (unsigned int i = 0; i < uuidBox.getSampleCount(); i++){
initVecs.push_back(uuidBox.getSample(i).InitializationVector);
}
}
}
/*LTS-END*/
moof.read(inFile);
if (feof(inFile)){return false;}
MP4::TRAF trafBox = moof.getChild<MP4::TRAF>();
for (size_t j = 0; j < trafBox.getContentCount(); j++){
if (trafBox.getContent(j).isType("trun")){
MP4::TRUN trunBox = (MP4::TRUN &)trafBox.getContent(j);
for (size_t i = 0; i < trunBox.getSampleInformationCount(); i++){
trunSamples.push_back(trunBox.getSampleInformation(i));
}
}
if (trafBox.getContent(j).isType("tfhd")){
tId = M.trackIDToIndex(((MP4::TFHD &)trafBox.getContent(j)).getTrackID(), getpid());
}
}
readBox("mdat", mdat);
if (mdat == ""){return false;}
return true;
MP4::skipBox(inFile);
return !feof(inFile);
}
void inputISMV::parseFragHeader(const unsigned int &trackId, const unsigned int &keyNum){
if (!myMeta.tracks.count(trackId) || (myMeta.tracks[trackId].keys.size() <= keyNum)){return;}
long long int lastPos = myMeta.tracks[trackId].keys[keyNum].getBpos();
long long int lastTime = myMeta.tracks[trackId].keys[keyNum].getTime() * 10000;
fseek(inFile, lastPos, SEEK_SET);
std::string boxRes;
readBox("moof", boxRes);
if (boxRes == ""){return;}
MP4::MOOF moof;
moof.read(boxRes);
void inputISMV::bufferFragmentData(size_t trackId, uint32_t keyNum){
INFO_MSG("Bpos seek for %zu/%" PRIu32, trackId, keyNum);
if (trackId == INVALID_TRACK_ID){return;}
DTSC::Keys keys(M.keys(trackId));
INFO_MSG("Key %" PRIu32 " / %zu", keyNum, keys.getEndValid());
if (keyNum >= keys.getEndValid()){return;}
uint64_t currentPosition = keys.getBpos(keyNum);
uint64_t currentTime = keys.getTime(keyNum) * 10000;
INFO_MSG("Bpos seek to %" PRIu64, currentPosition);
fseek(inFile, currentPosition, SEEK_SET);
MP4::MOOF moofBox;
moofBox.read(inFile);
MP4::TRAF trafBox = moofBox.getChild<MP4::TRAF>();
MP4::TRUN trunBox;
MP4::UUID_SampleEncryption uuidBox; /*LTS*/
for (unsigned int i = 0; i < moof.getContentCount(); i++){
if (moof.getContent(i).isType("traf")){
MP4::TRAF trafBox = (MP4::TRAF &)moof.getContent(i);
for (unsigned int j = 0; j < trafBox.getContentCount(); j++){
if (trafBox.getContent(j).isType("trun")){
trunBox = (MP4::TRUN &)trafBox.getContent(j);
}
if (trafBox.getContent(j).isType("tfhd")){
if (trackId != ((MP4::TFHD &)trafBox.getContent(j)).getTrackID()){
DEBUG_MSG(DLVL_FAIL, "Trackids do not match");
return;
}
}
/*LTS-START*/
if (trafBox.getContent(j).isType("uuid")){
if (((MP4::UUID &)trafBox.getContent(j)).getUUID() ==
"a2394f52-5a9b-4f14-a244-6c427c648df4"){
uuidBox = (MP4::UUID_SampleEncryption &)trafBox.getContent(j);
}
}
/*LTS-END*/
MP4::UUID_SampleEncryption uuidBox;
for (unsigned int j = 0; j < trafBox.getContentCount(); j++){
if (trafBox.getContent(j).isType("trun")){trunBox = (MP4::TRUN &)trafBox.getContent(j);}
if (trafBox.getContent(j).isType("tfhd")){
if (M.getID(trackId) != ((MP4::TFHD &)trafBox.getContent(j)).getTrackID()){
FAIL_MSG("Trackids do not match");
return;
}
}
}
lastPos = ftell(inFile) + 8;
currentPosition = ftell(inFile) + 8;
for (unsigned int i = 0; i < trunBox.getSampleInformationCount(); i++){
seekPos myPos;
myPos.position = lastPos;
myPos.position = currentPosition;
myPos.trackId = trackId;
myPos.time = lastTime;
myPos.time = currentTime;
myPos.duration = trunBox.getSampleInformation(i).sampleDuration;
myPos.size = trunBox.getSampleInformation(i).sampleSize;
if (trunBox.getFlags() & MP4::trunsampleOffsets){
@ -340,29 +253,9 @@ namespace Mist{
myPos.offset = 0;
}
myPos.isKeyFrame = (i == 0);
/*LTS-START*/
if (i <= uuidBox.getSampleCount()){myPos.iVec = uuidBox.getSample(i).InitializationVector;}
/*LTS-END*/
lastTime += trunBox.getSampleInformation(i).sampleDuration;
lastPos += trunBox.getSampleInformation(i).sampleSize;
currentTime += trunBox.getSampleInformation(i).sampleDuration;
currentPosition += trunBox.getSampleInformation(i).sampleSize;
buffered.insert(myPos);
}
}
void inputISMV::readBox(const char *type, std::string &result){
int pos = ftell(inFile);
char mp4Head[8];
fread(mp4Head, 8, 1, inFile);
fseek(inFile, pos, SEEK_SET);
if (memcmp(mp4Head + 4, type, 4)){
DEBUG_MSG(DLVL_FAIL, "No %.4s box found at position %d", type, pos);
result = "";
return;
}
unsigned int boxSize = (mp4Head[0] << 24) + (mp4Head[1] << 16) + (mp4Head[2] << 8) + mp4Head[3];
char *tmpBox = (char *)malloc(boxSize * sizeof(char));
fread(tmpBox, boxSize, 1, inFile);
result = std::string(tmpBox, boxSize);
free(tmpBox);
}
}// namespace Mist

View file

@ -3,6 +3,7 @@
#include <mist/mp4.h>
#include <mist/mp4_encryption.h>
#include <mist/mp4_generic.h>
#include <mist/util.h>
#include <set>
namespace Mist{
@ -11,12 +12,12 @@ namespace Mist{
if (time < rhs.time){return true;}
return (time == rhs.time && trackId < rhs.trackId);
}
long long int position;
int trackId;
long long int time;
long long int duration;
int size;
long long int offset;
uint64_t position;
size_t trackId;
uint64_t time;
uint64_t duration;
uint64_t size;
int64_t offset;
bool isKeyFrame;
std::string iVec;
};
@ -30,20 +31,19 @@ namespace Mist{
bool checkArguments();
bool preRun();
bool readHeader();
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
bool atKeyFrame();
virtual void getNext(size_t idx = INVALID_TRACK_ID);
virtual void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
FILE *inFile;
void parseMoov(MP4::MOOV &moovBox);
bool parseFrag(int &tId, std::vector<MP4::trunSampleInformation> &trunSamples,
std::vector<std::string> &initVecs, std::string &mdat);
void parseFragHeader(const unsigned int &trackId, const unsigned int &keyNum);
void readBox(const char *type, std::string &result);
bool readMoofSkipMdat(size_t &tId, std::vector<MP4::trunSampleInformation> &trunSamples);
void bufferFragmentData(size_t trackId, uint32_t keyNum);
std::set<seekPos> buffered;
std::map<int, int> lastKeyNum;
std::map<size_t, uint32_t> lastKeyNum;
Util::ResizeablePointer dataPointer;
};
}// namespace Mist

View file

@ -51,44 +51,45 @@ namespace Mist{
bool inputMP3::readHeader(){
if (!inFile){return false;}
myMeta = DTSC::Meta();
myMeta.tracks[1].trackID = 1;
myMeta.tracks[1].type = "audio";
myMeta.tracks[1].codec = "MP3";
meta.reInit(config->getString("streamname"));
size_t tNum = meta.addTrack();
meta.setID(tNum, tNum);
meta.setType(tNum, "audio");
meta.setCodec(tNum, "MP3");
// Create header file from MP3 data
char header[10];
fread(header, 10, 1, inFile); // Read a 10 byte header
if (header[0] == 'I' || header[1] == 'D' || header[2] == '3'){
size_t id3size = (((int)header[6] & 0x7F) << 21) | (((int)header[7] & 0x7F) << 14) |
(((int)header[8] & 0x7F) << 7) |
(header[9] & 0x7F) + 10 + ((header[5] & 0x10) ? 10 : 0);
((header[9] & 0x7F) + 10 + ((header[5] & 0x10) ? 10 : 0));
INFO_MSG("id3 size: %lu bytes", id3size);
fseek(inFile, id3size, SEEK_SET);
}else{
fseek(inFile, 0, SEEK_SET);
}
// Read the first mp3 header for bitrate and such
size_t filePos = ftell(inFile);
uint64_t filePos = ftell(inFile);
fread(header, 4, 1, inFile);
fseek(inFile, filePos, SEEK_SET);
Mpeg::MP2Info mp2Info = Mpeg::parseMP2Header(header);
myMeta.tracks[1].rate = mp2Info.sampleRate;
myMeta.tracks[1].channels = mp2Info.channels;
meta.setRate(tNum, mp2Info.sampleRate);
meta.setChannels(tNum, mp2Info.channels);
getNext();
while (thisPacket){
myMeta.update(thisPacket);
meta.update(thisPacket);
getNext();
}
fseek(inFile, 0, SEEK_SET);
timestamp = 0;
myMeta.toFile(config->getString("input") + ".dtsh");
M.toFile(config->getString("input") + ".dtsh");
return true;
}
void inputMP3::getNext(bool smart){
void inputMP3::getNext(size_t idx){
thisPacket.null();
static char packHeader[3000];
size_t filePos = ftell(inFile);
@ -107,7 +108,7 @@ namespace Mist{
}
}
if (!offset){
DEBUG_MSG(DLVL_FAIL, "Sync byte not found from offset %lu", filePos);
FAIL_MSG("Sync byte not found from offset %zu", filePos);
return;
}
filePos += offset;
@ -141,34 +142,16 @@ namespace Mist{
fseek(inFile, filePos + dataSize, SEEK_SET);
// Create a json value with the right data
static JSON::Value thisPack;
thisPack.null();
thisPack["trackid"] = 1;
thisPack["bpos"] = (uint64_t)filePos;
thisPack["data"] = std::string(packHeader, dataSize);
thisPack["time"] = timestamp;
// Write the json value to lastpack
std::string tmpStr = thisPack.toNetPacked();
thisPacket.reInit(tmpStr.data(), tmpStr.size());
thisPacket.genericFill(timestamp, 0, idx, packHeader, dataSize, filePos, false);
// Update the internal timestamp
timestamp += (sampleCount / (sampleRate / 1000));
}
void inputMP3::seek(int seekTime){
std::deque<DTSC::Key> &keys = myMeta.tracks[1].keys;
size_t seekPos = keys[0].getBpos();
for (unsigned int i = 0; i < keys.size(); i++){
if (keys[i].getTime() > seekTime){break;}
seekPos = keys[i].getBpos();
timestamp = keys[i].getTime();
}
timestamp = seekTime;
fseek(inFile, seekPos, SEEK_SET);
}
void inputMP3::trackSelect(std::string trackSpec){
// Ignore, do nothing
// MP3 Always has only 1 track, so we can't select much else..
void inputMP3::seek(uint64_t seekTime, size_t idx){
DTSC::Keys keys(M.keys(idx));
uint32_t keyNum = keys.getNumForTime(seekTime);
fseek(inFile, keys.getBpos(keyNum), SEEK_SET);
timestamp = keys.getTime(keyNum);
}
}// namespace Mist

View file

@ -21,9 +21,9 @@ namespace Mist{
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);
double timestamp;
FILE *inFile;

View file

@ -32,6 +32,7 @@ namespace Mist{
stcoBox.clear();
co64Box.clear();
stco64 = false;
trackId = 0;
}
uint64_t mp4TrackHeader::size(){return (stszBox.asBox() ? stszBox.getSampleCount() : 0);}
@ -45,6 +46,7 @@ namespace Mist{
MP4::MDIA mdiaBox = trakBox.getChild<MP4::MDIA>();
timeScale = mdiaBox.getChild<MP4::MDHD>().getTimeScale();
trackId = trakBox.getChild<MP4::TKHD>().getTrackID();
MP4::STBL stblBox = mdiaBox.getChild<MP4::MINF>().getChild<MP4::STBL>();
@ -148,6 +150,14 @@ namespace Mist{
size = stszBox.getEntrySize(index);
}
mp4TrackHeader &inputMP4::headerData(size_t trackID){
static mp4TrackHeader none;
for (std::deque<mp4TrackHeader>::iterator it = trackHeaders.begin(); it != trackHeaders.end(); it++){
if (it->trackId == trackID){return *it;}
}
return none;
}
inputMP4::inputMP4(Util::Config *cfg) : Input(cfg){
malSize = 4; // initialise data read buffer to 0;
data = (char *)malloc(malSize);
@ -200,9 +210,8 @@ namespace Mist{
return false;
}
uint32_t trackNo = 0;
// first we get the necessary header parts
size_t tNumber = 0;
while (!feof(inFile)){
std::string boxType = MP4::readBoxType(inFile);
if (boxType == "erro"){break;}
@ -213,7 +222,8 @@ namespace Mist{
std::deque<MP4::TRAK> trak = moovBox.getChildren<MP4::TRAK>();
for (std::deque<MP4::TRAK>::iterator trakIt = trak.begin(); trakIt != trak.end(); trakIt++){
headerData[++trackNo].read(*trakIt);
trackHeaders.push_back(mp4TrackHeader());
trackHeaders.rbegin()->read(*trakIt);
}
continue;
}
@ -228,7 +238,9 @@ namespace Mist{
if (readExistingHeader()){return true;}
HIGH_MSG("Not read existing header");
trackNo = 0;
meta.reInit(streamName);
tNumber = 0;
// Create header file from MP4 data
while (!feof(inFile)){
std::string boxType = MP4::readBoxType(inFile);
@ -241,96 +253,95 @@ namespace Mist{
HIGH_MSG("Obtained %zu trak Boxes", trak.size());
for (std::deque<MP4::TRAK>::iterator trakIt = trak.begin(); trakIt != trak.end(); trakIt++){
uint64_t trackNo = myMeta.tracks.size() + 1;
myMeta.tracks[trackNo].trackID = trackNo;
MP4::TKHD tkhdBox = trakIt->getChild<MP4::TKHD>();
if (tkhdBox.getWidth() > 0){
myMeta.tracks[trackNo].width = tkhdBox.getWidth();
myMeta.tracks[trackNo].height = tkhdBox.getHeight();
}
MP4::MDIA mdiaBox = trakIt->getChild<MP4::MDIA>();
MP4::MDHD mdhdBox = mdiaBox.getChild<MP4::MDHD>();
uint64_t timescale = mdhdBox.getTimeScale();
myMeta.tracks[trackNo].lang = mdhdBox.getLanguage();
std::string hdlrType = mdiaBox.getChild<MP4::HDLR>().getHandlerType();
if (hdlrType != "vide" && hdlrType != "soun" && hdlrType != "sbtl"){
headerData.erase(trackNo);
myMeta.tracks.erase(trackNo);
break;
INFO_MSG("Unsupported handler: %s", hdlrType.c_str());
continue;
}
tNumber = meta.addTrack();
MP4::TKHD tkhdBox = trakIt->getChild<MP4::TKHD>();
if (tkhdBox.getWidth() > 0){
meta.setWidth(tNumber, tkhdBox.getWidth());
meta.setHeight(tNumber, tkhdBox.getHeight());
}
meta.setID(tNumber, tkhdBox.getTrackID());
MP4::MDHD mdhdBox = mdiaBox.getChild<MP4::MDHD>();
uint64_t timescale = mdhdBox.getTimeScale();
meta.setLang(tNumber, mdhdBox.getLanguage());
MP4::STBL stblBox = mdiaBox.getChild<MP4::MINF>().getChild<MP4::STBL>();
MP4::STSD stsdBox = stblBox.getChild<MP4::STSD>();
MP4::Box sEntryBox = stsdBox.getEntry(0);
std::string sType = sEntryBox.getType();
HIGH_MSG("Found track %zu of type %s", trackNo, sType.c_str());
HIGH_MSG("Found track %zu of type %s", tNumber, sType.c_str());
if (sType == "avc1" || sType == "h264" || sType == "mp4v"){
MP4::VisualSampleEntry &vEntryBox = (MP4::VisualSampleEntry &)sEntryBox;
myMeta.tracks[trackNo].type = "video";
myMeta.tracks[trackNo].codec = "H264";
myMeta.tracks[trackNo].width = vEntryBox.getWidth();
myMeta.tracks[trackNo].height = vEntryBox.getHeight();
meta.setType(tNumber, "video");
meta.setCodec(tNumber, "H264");
if (!meta.getWidth(tNumber)){
meta.setWidth(tNumber, vEntryBox.getWidth());
meta.setHeight(tNumber, vEntryBox.getHeight());
}
MP4::Box initBox = vEntryBox.getCLAP();
if (initBox.isType("avcC")){
myMeta.tracks[trackNo].init.assign(initBox.payload(), initBox.payloadSize());
meta.setInit(tNumber, initBox.payload(), initBox.payloadSize());
}
initBox = vEntryBox.getPASP();
if (initBox.isType("avcC")){
myMeta.tracks[trackNo].init.assign(initBox.payload(), initBox.payloadSize());
meta.setInit(tNumber, initBox.payload(), initBox.payloadSize());
}
/// this is a hacky way around invalid FLV data (since it gets ignored nearly everywhere, but we do need correct data...
if (!myMeta.tracks[trackNo].width){
/// this is a hacky way around invalid FLV data (since it gets ignored nearly
/// everywhere, but we do need correct data...
if (!meta.getWidth(tNumber)){
h264::sequenceParameterSet sps;
sps.fromDTSCInit(myMeta.tracks[trackNo].init);
sps.fromDTSCInit(meta.getInit(tNumber));
h264::SPSMeta spsChar = sps.getCharacteristics();
myMeta.tracks[trackNo].width = spsChar.width;
myMeta.tracks[trackNo].height = spsChar.height;
meta.setWidth(tNumber, spsChar.width);
meta.setHeight(tNumber, spsChar.height);
}
}
if (sType == "hev1" || sType == "hvc1"){
MP4::VisualSampleEntry &vEntryBox = (MP4::VisualSampleEntry &)sEntryBox;
myMeta.tracks[trackNo].type = "video";
myMeta.tracks[trackNo].codec = "HEVC";
if (!myMeta.tracks[trackNo].width){
myMeta.tracks[trackNo].width = vEntryBox.getWidth();
myMeta.tracks[trackNo].height = vEntryBox.getHeight();
meta.setType(tNumber, "video");
meta.setCodec(tNumber, "HEVC");
if (!meta.getWidth(tNumber)){
meta.setWidth(tNumber, vEntryBox.getWidth());
meta.setHeight(tNumber, vEntryBox.getHeight());
}
MP4::Box initBox = vEntryBox.getCLAP();
if (initBox.isType("hvcC")){
myMeta.tracks[trackNo].init.assign(initBox.payload(), initBox.payloadSize());
meta.setInit(tNumber, initBox.payload(), initBox.payloadSize());
}
initBox = vEntryBox.getPASP();
if (initBox.isType("hvcC")){
myMeta.tracks[trackNo].init.assign(initBox.payload(), initBox.payloadSize());
meta.setInit(tNumber, initBox.payload(), initBox.payloadSize());
}
}
if (sType == "mp4a" || sType == "aac " || sType == "ac-3"){
MP4::AudioSampleEntry &aEntryBox = (MP4::AudioSampleEntry &)sEntryBox;
myMeta.tracks[trackNo].type = "audio";
myMeta.tracks[trackNo].channels = aEntryBox.getChannelCount();
myMeta.tracks[trackNo].rate = aEntryBox.getSampleRate();
meta.setType(tNumber, "audio");
meta.setChannels(tNumber, aEntryBox.getChannelCount());
meta.setRate(tNumber, aEntryBox.getSampleRate());
if (sType == "ac-3"){
myMeta.tracks[trackNo].codec = "AC3";
meta.setCodec(tNumber, "AC3");
}else{
MP4::ESDS esdsBox = (MP4::ESDS &)(aEntryBox.getCodecBox());
myMeta.tracks[trackNo].codec = esdsBox.getCodec();
myMeta.tracks[trackNo].init = esdsBox.getInitData();
meta.setCodec(tNumber, esdsBox.getCodec());
meta.setInit(tNumber, esdsBox.getInitData());
}
myMeta.tracks[trackNo].size = 16; ///\todo this might be nice to calculate from mp4 file;
meta.setSize(tNumber, 16); ///\todo this might be nice to calculate from mp4 file;
}
if (sType == "tx3g"){// plain text subtitles
myMeta.tracks[trackNo].type = "meta";
myMeta.tracks[trackNo].codec = "subtitle";
meta.setType(tNumber, "meta");
meta.setCodec(tNumber, "subtitle");
}
MP4::STSS stssBox = stblBox.getChild<MP4::STSS>();
@ -374,7 +385,7 @@ namespace Mist{
nextFirstChunk =
(stscIndex + 1 < stscCount ? stscBox.getSTSCEntry(stscIndex + 1).firstChunk - 1 : stcoCount);
}
BsetPart.keyframe = (myMeta.tracks[trackNo].type == "video" && stssIndex < stssCount &&
BsetPart.keyframe = (meta.getType(tNumber) == "video" && stssIndex < stssCount &&
stszIndex + 1 == stssBox.getSampleNumber(stssIndex));
if (BsetPart.keyframe){++stssIndex;}
// in bpos set
@ -417,12 +428,12 @@ namespace Mist{
long long packSendSize = 0;
packSendSize = 24 + (BsetPart.timeOffset ? 17 : 0) + (BsetPart.bpos ? 15 : 0) + 19 +
stszBox.getEntrySize(stszIndex) + 11 - 2 + 19;
myMeta.update(BsetPart.time, BsetPart.timeOffset, trackNo,
stszBox.getEntrySize(stszIndex) - 2, BsetPart.bpos, true, packSendSize);
meta.update(BsetPart.time, BsetPart.timeOffset, tNumber,
stszBox.getEntrySize(stszIndex) - 2, BsetPart.bpos, true, packSendSize);
}
}else{
myMeta.update(BsetPart.time, BsetPart.timeOffset, trackNo,
stszBox.getEntrySize(stszIndex), BsetPart.bpos, BsetPart.keyframe);
meta.update(BsetPart.time, BsetPart.timeOffset, tNumber,
stszBox.getEntrySize(stszIndex), BsetPart.bpos, BsetPart.keyframe);
}
}
}
@ -436,11 +447,11 @@ namespace Mist{
clearerr(inFile);
// outputting dtsh file
myMeta.toFile(config->getString("input") + ".dtsh");
M.toFile(config->getString("input") + ".dtsh");
return true;
}
void inputMP4::getNext(bool smart){// get next part from track in stream
void inputMP4::getNext(size_t idx){// get next part from track in stream
if (curPositions.empty()){
thisPacket.null();
return;
@ -450,17 +461,17 @@ namespace Mist{
curPositions.erase(curPositions.begin());
bool isKeyframe = false;
if (nextKeyframe[curPart.trackID] < myMeta.tracks[curPart.trackID].keys.size()){
DTSC::Keys keys(M.keys(curPart.trackID));
uint32_t nextKeyNum = nextKeyframe[curPart.trackID];
if (nextKeyNum < keys.getEndValid()){
// checking if this is a keyframe
if (myMeta.tracks[curPart.trackID].type == "video" &&
(long long int)curPart.time ==
myMeta.tracks[curPart.trackID].keys[(nextKeyframe[curPart.trackID])].getTime()){
if (meta.getType(curPart.trackID) == "video" && curPart.time == keys.getTime(nextKeyNum)){
isKeyframe = true;
}
// if a keyframe has passed, we find the next keyframe
if (myMeta.tracks[curPart.trackID].keys[(nextKeyframe[curPart.trackID])].getTime() <=
(long long int)curPart.time){
nextKeyframe[curPart.trackID]++;
if (keys.getTime(nextKeyNum) <= curPart.time){
++nextKeyframe[curPart.trackID];
++nextKeyNum;
}
}
if (fseeko(inFile, curPart.bpos, SEEK_SET)){
@ -478,85 +489,63 @@ namespace Mist{
return;
}
if (myMeta.tracks[curPart.trackID].codec == "subtitle"){
if (M.getCodec(curPart.trackID) == "subtitle"){
unsigned int txtLen = Bit::btohs(data);
if (!txtLen && false){
curPart.index++;
return getNext(smart);
// thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, " ", 1, 0/*Note: no bpos*/, isKeyframe);
}else{
static JSON::Value thisPack;
thisPack.null();
thisPack["trackid"] = (uint64_t)curPart.trackID;
thisPack["bpos"] = curPart.bpos; //(long long)fileSource.tellg();
thisPack["data"] = std::string(data + 2, txtLen);
thisPack["time"] = curPart.time;
if (curPart.duration){thisPack["duration"] = curPart.duration;}
thisPack["keyframe"] = true;
// Write the json value to lastpack
std::string tmpStr = thisPack.toNetPacked();
thisPacket.reInit(tmpStr.data(), tmpStr.size());
// return;
// thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, data+2, txtLen, 0/*Note: no bpos*/, isKeyframe);
return getNext(idx);
}
static JSON::Value thisPack;
thisPack.null();
thisPack["trackid"] = curPart.trackID;
thisPack["bpos"] = curPart.bpos; //(long long)fileSource.tellg();
thisPack["data"] = std::string(data + 2, txtLen);
thisPack["time"] = curPart.time;
if (curPart.duration){thisPack["duration"] = curPart.duration;}
thisPack["keyframe"] = true;
std::string tmpStr = thisPack.toNetPacked();
thisPacket.reInit(tmpStr.data(), tmpStr.size());
}else{
thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, data, curPart.size,
0 /*Note: no bpos*/, isKeyframe);
thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, data, curPart.size, 0, isKeyframe);
}
// get the next part for this track
curPart.index++;
if (curPart.index < headerData[curPart.trackID].size()){
headerData[curPart.trackID].getPart(curPart.index, curPart.bpos, curPart.size, curPart.time,
curPart.offset, curPart.duration);
if (curPart.index < headerData(M.getID(curPart.trackID)).size()){
headerData(M.getID(curPart.trackID))
.getPart(curPart.index, curPart.bpos, curPart.size, curPart.time, curPart.offset, curPart.duration);
curPositions.insert(curPart);
}
}
void inputMP4::seek(int seekTime){// seek to a point
void inputMP4::seek(uint64_t seekTime, size_t idx){// seek to a point
nextKeyframe.clear();
// for all tracks
curPositions.clear();
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
nextKeyframe[*it] = 0;
mp4PartTime addPart;
addPart.bpos = 0;
addPart.size = 0;
addPart.time = 0;
addPart.trackID = *it;
// for all indexes in those tracks
for (unsigned int i = 0; i < headerData[*it].size(); i++){
// if time > seekTime
headerData[*it].getPart(i, addPart.bpos, addPart.size, addPart.time, addPart.offset, addPart.duration);
// check for keyframe time in myMeta and update nextKeyframe
//
if (myMeta.tracks[*it].keys[(nextKeyframe[*it])].getTime() < addPart.time){
nextKeyframe[*it]++;
}
if (addPart.time >= seekTime){
addPart.index = i;
// use addPart thingy in time set and break
curPositions.insert(addPart);
break;
}// end if time > seektime
}// end for all indexes
}// rof all tracks
if (idx != INVALID_TRACK_ID){
handleSeek(seekTime, idx);
}else{
std::set<size_t> tracks = M.getValidTracks();
for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){
handleSeek(seekTime, *it);
}
}
}
void inputMP4::trackSelect(std::string trackSpec){
selectedTracks.clear();
long long int index;
while (trackSpec != ""){
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
VERYHIGH_MSG("Added track %d, index = %lld, (index == npos) = %d",
atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos);
if (index != std::string::npos){
trackSpec.erase(0, index + 1);
}else{
trackSpec = "";
void inputMP4::handleSeek(uint64_t seekTime, size_t idx){
nextKeyframe[idx] = 0;
mp4PartTime addPart;
addPart.trackID = idx;
// for all stsz samples in those tracks
mp4TrackHeader &thisHeader = headerData(M.getID(idx));
size_t headerDataSize = thisHeader.size();
DTSC::Keys keys(M.keys(idx));
for (size_t i = 0; i < headerDataSize; i++){
thisHeader.getPart(i, addPart.bpos, addPart.size, addPart.time, addPart.offset, addPart.duration);
if (keys.getTime(nextKeyframe[idx]) < addPart.time){nextKeyframe[idx]++;}
if (addPart.time >= seekTime){
addPart.index = i;
curPositions.insert(addPart);
break;
}
}
}

View file

@ -5,7 +5,7 @@
namespace Mist{
class mp4PartTime{
public:
mp4PartTime() : time(0), offset(0), trackID(0), bpos(0), size(0), index(0), duration(0){}
mp4PartTime() : time(0), duration(0), offset(0), trackID(0), bpos(0), size(0), index(0){}
bool operator<(const mp4PartTime &rhs) const{
if (time < rhs.time){return true;}
if (time > rhs.time){return false;}
@ -40,6 +40,7 @@ namespace Mist{
class mp4TrackHeader{
public:
mp4TrackHeader();
size_t trackId;
void read(MP4::TRAK &trakBox);
MP4::STCO stcoBox;
MP4::CO64 co64Box;
@ -80,21 +81,24 @@ namespace Mist{
bool preRun();
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);
void handleSeek(uint64_t seekTime, size_t idx);
FILE *inFile;
std::map<unsigned int, mp4TrackHeader> headerData;
mp4TrackHeader &headerData(size_t trackID);
std::deque<mp4TrackHeader> trackHeaders;
std::set<mp4PartTime> curPositions;
// remember last seeked keyframe;
std::map<unsigned int, unsigned int> nextKeyframe;
std::map<size_t, uint32_t> nextKeyframe;
// these next two variables keep a buffer for reading from filepointer inFile;
uint64_t malSize;
char *data; ///\todo rename this variable to a more sensible name, it is a temporary piece of memory to read from files
char *data; ///\todo rename this variable to a more sensible name, it is a temporary piece of
/// memory to read from files
};
}// namespace Mist

View file

@ -21,9 +21,8 @@ namespace Mist{
retval["time"] = time;
retval["trackid"] = tid;
std::string tmpString = "";
for (unsigned int i = 0; i < parts.size(); i++){tmpString += parts[i];}
for (size_t i = 0; i < parts.size(); i++){tmpString += parts[i];}
retval["data"] = tmpString;
// INFO_MSG("Setting bpos for packet on track %llu, time %llu, to %llu", tid, time, bytepos);
retval["bpos"] = bytepos;
if (myCodec == OGG::THEORA){
if (!theora::isHeader(tmpString.data(), tmpString.size())){
@ -34,12 +33,6 @@ namespace Mist{
return retval;
}
/*
unsigned long oggTrack::getBlockSize(unsigned int vModeIndex){//WTF!!?
return blockSize[vModes[vModeIndex].blockFlag];
}
*/
inputOGG::inputOGG(Util::Config *cfg) : Input(cfg){
capa["name"] = "OGG";
capa["desc"] = "This input allows streaming of OGG files as Video on Demand.";
@ -68,70 +61,74 @@ namespace Mist{
///\todo check if all trackID (tid) instances are replaced with bitstream serial numbers
void inputOGG::parseBeginOfStream(OGG::Page &bosPage){
// long long int tid = snum2tid.size() + 1;
unsigned int tid = bosPage.getBitstreamSerialNumber();
size_t tid = bosPage.getBitstreamSerialNumber();
size_t idx = M.trackIDToIndex(tid, getpid());
if (idx == INVALID_TRACK_ID){idx = meta.addTrack();}
if (memcmp(bosPage.getSegment(0) + 1, "theora", 6) == 0){
theora::header tmpHead((char *)bosPage.getSegment(0), bosPage.getSegmentLen(0));
oggTracks[tid].codec = OGG::THEORA;
oggTracks[tid].msPerFrame = (double)(tmpHead.getFRD() * 1000) / (double)tmpHead.getFRN(); // this should be: 1000/( tmpHead.getFRN()/ tmpHead.getFRD() )
oggTracks[tid].KFGShift = tmpHead.getKFGShift(); // store KFGShift for granule calculations
myMeta.tracks[tid].type = "video";
myMeta.tracks[tid].codec = "theora";
myMeta.tracks[tid].trackID = tid;
myMeta.tracks[tid].fpks = (tmpHead.getFRN() * 1000) / tmpHead.getFRD();
myMeta.tracks[tid].height = tmpHead.getPICH();
myMeta.tracks[tid].width = tmpHead.getPICW();
if (!myMeta.tracks[tid].init.size()){
myMeta.tracks[tid].init = (char)((bosPage.getPayloadSize() >> 8) & 0xFF);
myMeta.tracks[tid].init += (char)(bosPage.getPayloadSize() & 0xFF);
myMeta.tracks[tid].init.append(bosPage.getSegment(0), bosPage.getSegmentLen(0));
oggTracks[idx].codec = OGG::THEORA;
oggTracks[idx].msPerFrame = (double)(tmpHead.getFRD() * 1000) / (double)tmpHead.getFRN(); // this should be: 1000/( tmpHead.getFRN()/ tmpHead.getFRD() )
oggTracks[idx].KFGShift = tmpHead.getKFGShift(); // store KFGShift for granule calculations
meta.setType(idx, "video");
meta.setCodec(idx, "theora");
meta.setID(idx, tid);
meta.setFpks(idx, (double)(tmpHead.getFRN() * 1000) / tmpHead.getFRD());
meta.setHeight(idx, tmpHead.getPICH());
meta.setWidth(idx, tmpHead.getPICW());
if (!M.getInit(idx).size()){
std::string init = " ";
Bit::htobs((char *)init.data(), bosPage.getPayloadSize());
init.append(bosPage.getSegment(0), bosPage.getSegmentLen(0));
meta.setInit(idx, init);
}
INFO_MSG("Track %lu is %s", tid, myMeta.tracks[tid].codec.c_str());
INFO_MSG("Track with id %zu is %s", tid, M.getCodec(tid).c_str());
}
if (memcmp(bosPage.getSegment(0) + 1, "vorbis", 6) == 0){
vorbis::header tmpHead((char *)bosPage.getSegment(0), bosPage.getSegmentLen(0));
oggTracks[tid].codec = OGG::VORBIS;
oggTracks[tid].msPerFrame = (double)1000.0f / tmpHead.getAudioSampleRate();
DEBUG_MSG(DLVL_DEVEL, "vorbis trackID: %d msperFrame %f ", tid, oggTracks[tid].msPerFrame);
oggTracks[tid].channels = tmpHead.getAudioChannels();
oggTracks[tid].blockSize[0] = 1 << tmpHead.getBlockSize0();
oggTracks[tid].blockSize[1] = 1 << tmpHead.getBlockSize1();
oggTracks[idx].codec = OGG::VORBIS;
oggTracks[idx].msPerFrame = (double)1000.0f / tmpHead.getAudioSampleRate();
oggTracks[idx].channels = tmpHead.getAudioChannels();
oggTracks[idx].blockSize[0] = 1 << tmpHead.getBlockSize0();
oggTracks[idx].blockSize[1] = 1 << tmpHead.getBlockSize1();
DEVEL_MSG("vorbis trackID: %zu msperFrame %f ", tid, oggTracks[idx].msPerFrame);
// Abusing .contBuffer for temporarily storing the idHeader
bosPage.getSegment(0, oggTracks[tid].contBuffer);
bosPage.getSegment(0, oggTracks[idx].contBuffer);
myMeta.tracks[tid].type = "audio";
myMeta.tracks[tid].codec = "vorbis";
myMeta.tracks[tid].rate = tmpHead.getAudioSampleRate();
myMeta.tracks[tid].trackID = tid;
myMeta.tracks[tid].channels = tmpHead.getAudioChannels();
INFO_MSG("Track %lu is %s", tid, myMeta.tracks[tid].codec.c_str());
meta.setType(idx, "audio");
meta.setCodec(idx, "vorbis");
meta.setRate(idx, tmpHead.getAudioSampleRate());
meta.setID(idx, tid);
meta.setChannels(idx, tmpHead.getAudioChannels());
INFO_MSG("Track with id %zu is %s", tid, M.getCodec(idx).c_str());
}
if (memcmp(bosPage.getSegment(0), "OpusHead", 8) == 0){
oggTracks[tid].codec = OGG::OPUS;
myMeta.tracks[tid].type = "audio";
myMeta.tracks[tid].codec = "opus";
myMeta.tracks[tid].rate = 48000;
myMeta.tracks[tid].trackID = tid;
myMeta.tracks[tid].init.assign(bosPage.getSegment(0), bosPage.getSegmentLen(0));
myMeta.tracks[tid].channels = myMeta.tracks[tid].init[9];
INFO_MSG("Track %lu is %s", tid, myMeta.tracks[tid].codec.c_str());
oggTracks[idx].codec = OGG::OPUS;
meta.setType(idx, "audio");
meta.setCodec(idx, "opus");
meta.setRate(idx, 48000);
meta.setID(idx, tid);
meta.setInit(idx, bosPage.getSegment(0), bosPage.getSegmentLen(0));
meta.setChannels(idx, M.getInit(idx)[9]);
INFO_MSG("Track with id %zu is %s", tid, M.getCodec(idx).c_str());
}
}
bool inputOGG::readHeader(){
meta.reInit(config->getString("streamname"), true);
OGG::Page myPage;
fseek(inFile, 0, SEEK_SET);
while (myPage.read(inFile)){// assumes all headers are sent before any data
unsigned int tid = myPage.getBitstreamSerialNumber();
size_t tid = myPage.getBitstreamSerialNumber();
size_t idx = M.trackIDToIndex(tid, getpid());
if (myPage.getHeaderType() & OGG::BeginOfStream){
parseBeginOfStream(myPage);
INFO_MSG("Read BeginOfStream for track %d", tid);
INFO_MSG("Read BeginOfStream for track %zu", tid);
continue; // Continue reading next pages
}
bool readAllHeaders = true;
for (std::map<long unsigned int, OGG::oggTrack>::iterator it = oggTracks.begin();
it != oggTracks.end(); it++){
for (std::map<size_t, OGG::oggTrack>::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){
if (!it->second.parsedHeaders){
readAllHeaders = false;
break;
@ -139,143 +136,142 @@ namespace Mist{
}
if (readAllHeaders){break;}
// INFO_MSG("tid: %d",tid);
// Parsing headers
if (myMeta.tracks[tid].codec == "theora"){
for (unsigned int i = 0; i < myPage.getAllSegments().size(); i++){
unsigned long len = myPage.getSegmentLen(i);
if (M.getCodec(idx) == "theora"){
for (size_t i = 0; i < myPage.getAllSegments().size(); i++){
size_t len = myPage.getSegmentLen(i);
theora::header tmpHead((char *)myPage.getSegment(i), len);
if (!tmpHead.isHeader()){// not copying the header anymore, should this check isHeader?
DEBUG_MSG(DLVL_FAIL, "Theora Header read failed!");
FAIL_MSG("Theora Header read failed!");
return false;
}
switch (tmpHead.getHeaderType()){
// Case 0 is being handled by parseBeginOfStream
case 1:{
myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF);
myMeta.tracks[tid].init += (char)(len & 0xFF);
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
std::string init = M.getInit(idx);
init += (char)((len >> 8) & 0xFF);
init += (char)(len & 0xFF);
init.append(myPage.getSegment(i), len);
meta.setInit(idx, init);
break;
}
case 2:{
myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF);
myMeta.tracks[tid].init += (char)(len & 0xFF);
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
oggTracks[tid].lastGran = 0;
oggTracks[tid].parsedHeaders = true;
std::string init = M.getInit(idx);
init += (char)((len >> 8) & 0xFF);
init += (char)(len & 0xFF);
init.append(myPage.getSegment(i), len);
meta.setInit(idx, init);
oggTracks[idx].lastGran = 0;
oggTracks[idx].parsedHeaders = true;
break;
}
}
}
}
if (myMeta.tracks[tid].codec == "vorbis"){
for (unsigned int i = 0; i < myPage.getAllSegments().size(); i++){
unsigned long len = myPage.getSegmentLen(i);
if (M.getCodec(idx) == "vorbis"){
for (size_t i = 0; i < myPage.getAllSegments().size(); i++){
size_t len = myPage.getSegmentLen(i);
vorbis::header tmpHead((char *)myPage.getSegment(i), len);
if (!tmpHead.isHeader()){
DEBUG_MSG(DLVL_FAIL, "Header read failed!");
FAIL_MSG("Header read failed!");
return false;
}
switch (tmpHead.getHeaderType()){
// Case 1 is being handled by parseBeginOfStream
case 3:{
// we have the first header stored in contBuffer
myMeta.tracks[tid].init += (char)0x02;
std::string init = M.getInit(idx);
init += (char)0x02;
// ID header size
for (unsigned int j = 0; j < (oggTracks[tid].contBuffer.size() / 255); j++){
myMeta.tracks[tid].init += (char)0xFF;
for (size_t j = 0; j < (oggTracks[idx].contBuffer.size() / 255); j++){
init += (char)0xFF;
}
myMeta.tracks[tid].init += (char)(oggTracks[tid].contBuffer.size() % 255);
init += (char)(oggTracks[idx].contBuffer.size() % 255);
// Comment header size
for (unsigned int j = 0; j < (len / 255); j++){
myMeta.tracks[tid].init += (char)0xFF;
}
myMeta.tracks[tid].init += (char)(len % 255);
myMeta.tracks[tid].init += oggTracks[tid].contBuffer;
oggTracks[tid].contBuffer.clear();
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
for (size_t j = 0; j < (len / 255); j++){init += (char)0xFF;}
init += (char)(len % 255);
init += oggTracks[idx].contBuffer;
oggTracks[idx].contBuffer.clear();
init.append(myPage.getSegment(i), len);
meta.setInit(idx, init);
break;
}
case 5:{
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels);
oggTracks[tid].parsedHeaders = true;
std::string init = M.getInit(idx);
init.append(myPage.getSegment(i), len);
meta.setInit(idx, init);
oggTracks[idx].vModes = tmpHead.readModeDeque(oggTracks[idx].channels);
oggTracks[idx].parsedHeaders = true;
break;
}
}
}
}
if (myMeta.tracks[tid].codec == "opus"){oggTracks[tid].parsedHeaders = true;}
if (M.getCodec(idx) == "opus"){oggTracks[idx].parsedHeaders = true;}
}
for (std::map<long unsigned int, OGG::oggTrack>::iterator it = oggTracks.begin();
it != oggTracks.end(); it++){
for (std::map<size_t, OGG::oggTrack>::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){
fseek(inFile, 0, SEEK_SET);
INFO_MSG("Finding first data for track %lu", it->first);
INFO_MSG("Finding first data for track %zu", it->first);
position tmp = seekFirstData(it->first);
if (tmp.trackID){
currentPositions.insert(tmp);
}else{
INFO_MSG("missing track: %lu", it->first);
}
currentPositions.insert(tmp);
}
getNext();
while (thisPacket){
myMeta.update(thisPacket);
meta.update(thisPacket);
getNext();
}
myMeta.toFile(config->getString("input") + ".dtsh");
meta.toFile(config->getString("input") + ".dtsh");
return true;
}
position inputOGG::seekFirstData(long long unsigned int tid){
position inputOGG::seekFirstData(size_t idx){
fseek(inFile, 0, SEEK_SET);
position res;
res.time = 0;
res.trackID = tid;
res.trackID = idx;
res.segmentNo = 0;
bool readSuccesfull = true;
bool quitloop = false;
while (!quitloop){
quitloop = true;
res.bytepos = ftell(inFile);
readSuccesfull = oggTracks[tid].myPage.read(inFile);
readSuccesfull = oggTracks[idx].myPage.read(inFile);
if (!readSuccesfull){
quitloop = true; // break :(
break;
}
if (oggTracks[tid].myPage.getBitstreamSerialNumber() != tid){
if (oggTracks[idx].myPage.getBitstreamSerialNumber() != M.getID(idx)){
quitloop = false;
continue;
}
if (oggTracks[tid].myPage.getHeaderType() != OGG::Plain){
if (oggTracks[idx].myPage.getHeaderType() != OGG::Plain){
quitloop = false;
continue;
}
if (oggTracks[tid].codec == OGG::OPUS){
if (std::string(oggTracks[tid].myPage.getSegment(0), 2) == "Op"){quitloop = false;}
if (oggTracks[idx].codec == OGG::OPUS){
if (std::string(oggTracks[idx].myPage.getSegment(0), 2) == "Op"){quitloop = false;}
}
if (oggTracks[tid].codec == OGG::VORBIS){
vorbis::header tmpHead((char *)oggTracks[tid].myPage.getSegment(0),
oggTracks[tid].myPage.getSegmentLen(0));
if (oggTracks[idx].codec == OGG::VORBIS){
vorbis::header tmpHead((char *)oggTracks[idx].myPage.getSegment(0),
oggTracks[idx].myPage.getSegmentLen(0));
if (tmpHead.isHeader()){quitloop = false;}
}
if (oggTracks[tid].codec == OGG::THEORA){
theora::header tmpHead((char *)oggTracks[tid].myPage.getSegment(0),
oggTracks[tid].myPage.getSegmentLen(0));
if (oggTracks[idx].codec == OGG::THEORA){
theora::header tmpHead((char *)oggTracks[idx].myPage.getSegment(0),
oggTracks[idx].myPage.getSegmentLen(0));
if (tmpHead.isHeader()){quitloop = false;}
}
}// while ( oggTracks[tid].myPage.getHeaderType() != OGG::Plain && readSuccesfull && oggTracks[tid].myPage.getBitstreamSerialNumber() != tid);
INFO_MSG("seek first bytepos: %llu tid: %llu oggTracks[tid].myPage.getHeaderType(): %d ",
res.bytepos, tid, oggTracks[tid].myPage.getHeaderType());
}
INFO_MSG("seek first bytepos: %" PRIu64 " tid: %zu oggTracks[idx].myPage.getHeaderType(): %d ",
res.bytepos, idx, oggTracks[idx].myPage.getHeaderType());
if (!readSuccesfull){res.trackID = 0;}
return res;
}
void inputOGG::getNext(bool smart){
void inputOGG::getNext(size_t idx){
if (!currentPositions.size()){
thisPacket.null();
return;
@ -287,7 +283,7 @@ namespace Mist{
thisSegment.tid = curPos.trackID;
thisSegment.time = curPos.time;
thisSegment.bytepos = curPos.bytepos + curPos.segmentNo;
unsigned int oldSegNo = curPos.segmentNo;
size_t oldSegNo = curPos.segmentNo;
fseek(inFile, curPos.bytepos, SEEK_SET);
OGG::Page curPage;
curPage.read(inFile);
@ -296,7 +292,7 @@ namespace Mist{
bool readFullPacket = false;
if (curPos.segmentNo == curPage.getAllSegments().size() - 1){
OGG::Page tmpPage;
unsigned int bPos;
uint64_t bPos;
while (!readFullPacket){
bPos = ftell(inFile); //<-- :(
if (!tmpPage.read(inFile)){break;}
@ -315,11 +311,11 @@ namespace Mist{
}else{
curPos.segmentNo++;
// if (oggTracks[thisSegment.tid].codec == OGG::THEORA && curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull)
// && curPos.segmentNo == curPage.getAllSegments().size() - 1){//if the next segment is the last one on the page, the (theora) granule should be used to sync the time for the current segment
if ((oggTracks[thisSegment.tid].codec == OGG::THEORA || oggTracks[thisSegment.tid].codec == OGG::VORBIS) &&
if ((oggTracks[curPos.trackID].codec == OGG::THEORA || oggTracks[curPos.trackID].codec == OGG::VORBIS) &&
curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull) &&
curPos.segmentNo == curPage.getAllSegments().size() - 1){// if the next segment is the last one on the page, the (theora) granule should be used to sync the time for the current segment
curPos.segmentNo == curPage.getAllSegments().size() -
1){// if the next segment is the last one on the page, the (theora) granule
// should be used to sync the time for the current segment
OGG::Page tmpPage;
while (tmpPage.read(inFile) && tmpPage.getBitstreamSerialNumber() != thisSegment.tid){}
if ((tmpPage.getBitstreamSerialNumber() == thisSegment.tid) && tmpPage.getHeaderType() == OGG::Continued){
@ -328,37 +324,36 @@ namespace Mist{
}
readFullPacket = true;
}
std::string tmpStr = thisSegment.toJSON(oggTracks[thisSegment.tid].codec).toNetPacked();
std::string tmpStr = thisSegment.toJSON(oggTracks[curPos.trackID].codec).toNetPacked();
thisPacket.reInit(tmpStr.data(), tmpStr.size());
if (oggTracks[thisSegment.tid].codec == OGG::VORBIS){
unsigned long blockSize = 0;
if (oggTracks[curPos.trackID].codec == OGG::VORBIS){
size_t blockSize = 0;
Utils::bitstreamLSBF packet;
packet.append((char *)curPage.getSegment(oldSegNo), curPage.getSegmentLen(oldSegNo));
if (!packet.get(1)){
// Read index first
unsigned long vModeIndex = packet.get(vorbis::ilog(oggTracks[thisSegment.tid].vModes.size() - 1));
size_t vModeIndex = packet.get(vorbis::ilog(oggTracks[curPos.trackID].vModes.size() - 1));
blockSize =
oggTracks[thisSegment.tid].blockSize[oggTracks[thisSegment.tid].vModes[vModeIndex].blockFlag]; // almost readable.
oggTracks[curPos.trackID].blockSize[oggTracks[curPos.trackID].vModes[vModeIndex].blockFlag]; // almost
// readable.
}else{
DEBUG_MSG(DLVL_WARN, "Packet type != 0");
WARN_MSG("Packet type != 0");
}
curPos.time += oggTracks[thisSegment.tid].msPerFrame * (blockSize / oggTracks[thisSegment.tid].channels);
}else if (oggTracks[thisSegment.tid].codec == OGG::THEORA){
curPos.time += oggTracks[curPos.trackID].msPerFrame * (blockSize / oggTracks[curPos.trackID].channels);
}else if (oggTracks[curPos.trackID].codec == OGG::THEORA){
if (lastCompleteSegment == true && curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull)){// this segment should be used to sync time using granule
long long unsigned int parseGranuleUpper =
curPage.getGranulePosition() >> oggTracks[thisSegment.tid].KFGShift;
long long unsigned int parseGranuleLower(curPage.getGranulePosition() &
((1 << oggTracks[thisSegment.tid].KFGShift) - 1));
thisSegment.time =
oggTracks[thisSegment.tid].msPerFrame * (parseGranuleUpper + parseGranuleLower - 1);
uint64_t parseGranuleUpper = curPage.getGranulePosition() >> oggTracks[curPos.trackID].KFGShift;
uint64_t parseGranuleLower(curPage.getGranulePosition() &
((1 << oggTracks[curPos.trackID].KFGShift) - 1));
thisSegment.time = oggTracks[curPos.trackID].msPerFrame * (parseGranuleUpper + parseGranuleLower - 1);
curPos.time = thisSegment.time;
std::string tmpStr = thisSegment.toJSON(oggTracks[thisSegment.tid].codec).toNetPacked();
std::string tmpStr = thisSegment.toJSON(oggTracks[curPos.trackID].codec).toNetPacked();
thisPacket.reInit(tmpStr.data(), tmpStr.size());
// INFO_MSG("thisTime: %d", thisPacket.getTime());
}
curPos.time += oggTracks[thisSegment.tid].msPerFrame;
}else if (oggTracks[thisSegment.tid].codec == OGG::OPUS){
curPos.time += oggTracks[curPos.trackID].msPerFrame;
}else if (oggTracks[curPos.trackID].codec == OGG::OPUS){
if (thisSegment.parts.size()){
curPos.time += Opus::Opus_getDuration(thisSegment.parts.front().data());
}
@ -366,21 +361,22 @@ namespace Mist{
if (readFullPacket){currentPositions.insert(curPos);}
}// getnext()
long long unsigned int inputOGG::calcGranuleTime(unsigned long tid, long long unsigned int granule){
switch (oggTracks[tid].codec){
uint64_t inputOGG::calcGranuleTime(size_t tid, uint64_t granule){
size_t idx = M.trackIDToIndex(tid, getpid());
switch (oggTracks[idx].codec){
case OGG::VORBIS:
return granule * oggTracks[tid].msPerFrame; //= samples * samples per second
return granule * oggTracks[idx].msPerFrame; //= samples * samples per second
break;
case OGG::OPUS:
return granule / 48; // always 48kHz
break;
case OGG::THEORA:{
long long unsigned int parseGranuleUpper = granule >> oggTracks[tid].KFGShift;
long long unsigned int parseGranuleLower = (granule & ((1 << oggTracks[tid].KFGShift) - 1));
return (parseGranuleUpper + parseGranuleLower) * oggTracks[tid].msPerFrame; //= frames * msPerFrame
uint64_t parseGranuleUpper = granule >> oggTracks[idx].KFGShift;
uint64_t parseGranuleLower = (granule & ((1 << oggTracks[idx].KFGShift) - 1));
return (parseGranuleUpper + parseGranuleLower) * oggTracks[idx].msPerFrame; //= frames * msPerFrame
break;
}
default: DEBUG_MSG(DLVL_WARN, "Unknown codec, can not calculate time from granule"); break;
default: WARN_MSG("Unknown codec, can not calculate time from granule"); break;
}
return 0;
}
@ -398,27 +394,28 @@ namespace Mist{
}
#endif
void inputOGG::seek(int seekTime){
void inputOGG::seek(uint64_t seekTime, size_t idx){
currentPositions.clear();
DEBUG_MSG(DLVL_MEDIUM, "Seeking to %dms", seekTime);
MEDIUM_MSG("Seeking to %" PRIu64 "ms", seekTime);
// for every track
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
// find first keyframe before keyframe with ms > seektime
position tmpPos;
tmpPos.trackID = *it;
tmpPos.time = myMeta.tracks[*it].keys.begin()->getTime();
tmpPos.bytepos = myMeta.tracks[*it].keys.begin()->getBpos();
for (std::deque<DTSC::Key>::iterator ot = myMeta.tracks[*it].keys.begin();
ot != myMeta.tracks[*it].keys.end(); ot++){
if (ot->getTime() > seekTime){
tmpPos.trackID = it->first;
DTSC::Keys keys(M.keys(it->first));
tmpPos.time = keys.getTime(keys.getFirstValid());
tmpPos.bytepos = keys.getBpos(keys.getFirstValid());
for (size_t i = keys.getFirstValid(); i < keys.getEndValid(); ++i){
if (keys.getTime(i) > seekTime){
break;
}else{
tmpPos.time = ot->getTime();
tmpPos.bytepos = ot->getBpos();
tmpPos.time = keys.getTime(i);
tmpPos.bytepos = keys.getBpos(i);
}
}
INFO_MSG("Found %dms for track %lu at %llu bytepos %llu", seekTime, *it, tmpPos.time, tmpPos.bytepos);
INFO_MSG("Found %" PRIu64 "ms for track %zu at %" PRIu64 " bytepos %" PRIu64, seekTime,
it->first, tmpPos.time, tmpPos.bytepos);
int backChrs = std::min((uint64_t)280, tmpPos.bytepos - 1);
fseek(inFile, tmpPos.bytepos - backChrs, SEEK_SET);
char buffer[300];
@ -428,28 +425,15 @@ namespace Mist{
loc = (char *)memrchr(buffer, 'O', (loc - buffer) - 1); // seek reverse
}
if (!loc){
INFO_MSG("Unable to find a page boundary starting @ %llu, track %lu", tmpPos.bytepos, *it);
INFO_MSG("Unable to find a page boundary starting @ %" PRIu64 ", track %zu", tmpPos.bytepos, it->first);
continue;
}
tmpPos.segmentNo = backChrs - (loc - buffer);
tmpPos.bytepos -= tmpPos.segmentNo;
INFO_MSG("Track %lu, segment %llu found at bytepos %llu", *it, tmpPos.segmentNo, tmpPos.bytepos);
INFO_MSG("Track %zu, segment %zu found at bytepos %" PRIu64, it->first, tmpPos.segmentNo,
tmpPos.bytepos);
currentPositions.insert(tmpPos);
}
}
void inputOGG::trackSelect(std::string trackSpec){
selectedTracks.clear();
size_t index;
while (trackSpec != ""){
index = trackSpec.find(' ');
selectedTracks.insert(atoll(trackSpec.substr(0, index).c_str()));
if (index != std::string::npos){
trackSpec.erase(0, index + 1);
}else{
trackSpec = "";
}
}
}
}// namespace Mist

View file

@ -6,7 +6,7 @@ namespace Mist{
struct segPart{
char *segData;
unsigned int len;
size_t len;
};
class segment{
@ -25,43 +25,15 @@ namespace Mist{
struct position{
bool operator<(const position &rhs) const{
if (time < rhs.time){
return true;
}else{
if (time == rhs.time){
if (trackID < rhs.trackID){return true;}
}
}
return false;
if (time < rhs.time){return true;}
if (time > rhs.time){return false;}
return trackID < rhs.trackID;
}
uint64_t trackID;
uint64_t time;
uint64_t bytepos;
uint64_t segmentNo;
};
/*
class oggTrack{
public:
oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0){}
codecType codec;
std::string contBuffer;//buffer for continuing pages
segment myBuffer;
double lastTime;
long long unsigned int lastGran;
bool parsedHeaders;
double msPerFrame;
long long unsigned int lastPageOffset;
OGG::Page myPage;
unsigned int nxtSegment;
//Codec specific elements
//theora
theora::header idHeader;
//vorbis
std::deque<vorbis::mode> vModes;
char channels;
unsigned long blockSize[2];
unsigned long getBlockSize(unsigned int vModeIndex);
};*/
class inputOGG : public Input{
public:
@ -72,18 +44,17 @@ namespace Mist{
bool checkArguments();
bool preRun();
bool readHeader();
position seekFirstData(long long unsigned int tid);
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
position seekFirstData(size_t tid);
void getNext(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
void parseBeginOfStream(OGG::Page &bosPage);
std::set<position> currentPositions;
FILE *inFile;
std::map<long unsigned int, OGG::oggTrack> oggTracks; // this remembers all metadata for every track
std::set<segment> sortedSegments; // probably not needing this
long long unsigned int calcGranuleTime(unsigned long tid, long long unsigned int granule);
long long unsigned int calcSegmentDuration(unsigned long tid, std::string &segment);
std::map<size_t, OGG::oggTrack> oggTracks; // this remembers all metadata for every track
std::set<segment> sortedSegments; // probably not needing this
uint64_t calcGranuleTime(size_t tid, uint64_t granule);
uint64_t calcSegmentDuration(size_t tid, std::string &segment);
};
}// namespace Mist

View file

@ -15,7 +15,7 @@ void insertRTP(const uint64_t track, const RTP::Packet &p){
///\param data The RTP Packet that needs to be sent
///\param len The size of data
///\param channel Not used here, but is kept for compatibility with sendTCP
void sendUDP(void *socket, char *data, unsigned int len, unsigned int channel){
void sendUDP(void *socket, const char *data, size_t len, uint8_t channel){
((Socket::UDPConnection *)socket)->SendNow(data, len);
if (mainConn){mainConn->addUp(len);}
}
@ -27,8 +27,10 @@ namespace Mist{
InputRTSP::InputRTSP(Util::Config *cfg) : Input(cfg){
needAuth = false;
setPacketOffset = false;
packetOffset = 0;
TCPmode = true;
sdpState.myMeta = &myMeta;
sdpState.myMeta = &meta;
sdpState.incomingPacketCallback = incomingPacket;
classPointer = this;
standAlone = false;
@ -153,28 +155,29 @@ namespace Mist{
}
if (sdpState.tracks.size()){
bool atLeastOne = false;
for (std::map<uint32_t, SDP::Track>::iterator it = sdpState.tracks.begin();
for (std::map<size_t, SDP::Track>::iterator it = sdpState.tracks.begin();
it != sdpState.tracks.end(); ++it){
transportSet = false;
extraHeaders.clear();
extraHeaders["Transport"] = it->second.generateTransport(it->first, url.host, TCPmode);
sendCommand("SETUP", HTTP::URL(url.getUrl() + "/").link(it->second.control).getUrl(), "", &extraHeaders);
lastRequestedSetup = HTTP::URL(url.getUrl() + "/").link(it->second.control).getUrl();
sendCommand("SETUP", lastRequestedSetup, "", &extraHeaders);
if (tcpCon && transportSet){
atLeastOne = true;
continue;
}
if (!atLeastOne && tcpCon){
INFO_MSG("Failed to set up transport for track %s, switching transports...",
myMeta.tracks[it->first].getIdentifier().c_str());
M.getTrackIdentifier(it->first).c_str());
TCPmode = !TCPmode;
extraHeaders["Transport"] = it->second.generateTransport(it->first, url.host, TCPmode);
sendCommand("SETUP", HTTP::URL(url.getUrl() + "/").link(it->second.control).getUrl(), "", &extraHeaders);
sendCommand("SETUP", lastRequestedSetup, "", &extraHeaders);
}
if (tcpCon && transportSet){
atLeastOne = true;
continue;
}
FAIL_MSG("Could not setup track %s!", myMeta.tracks[it->first].getIdentifier().c_str());
FAIL_MSG("Could not setup track %s!", M.getTrackIdentifier(it->first).c_str());
tcpCon.close();
return;
}
@ -183,11 +186,7 @@ namespace Mist{
extraHeaders.clear();
extraHeaders["Range"] = "npt=0.000-";
sendCommand("PLAY", url.getUrl(), "", &extraHeaders);
if (!TCPmode){
connectedAt = Util::epoch() + 2208988800ll;
}else{
tcpCon.setBlocking(true);
}
if (TCPmode){tcpCon.setBlocking(true);}
}
void InputRTSP::closeStreamSource(){
@ -199,13 +198,9 @@ namespace Mist{
IPC::sharedClient statsPage = IPC::sharedClient(SHM_STATISTICS, STAT_EX_SIZE, true);
uint64_t startTime = Util::epoch();
uint64_t lastPing = Util::bootSecs();
uint64_t lastSecs = 0;
while (config->is_active && nProxy.userClient.isAlive() && parsePacket()){
while (keepAlive() && parsePacket()){
handleUDP();
// keep going
nProxy.userClient.keepAlive();
uint64_t currSecs = Util::bootSecs();
if (currSecs - lastPing > 30){
if (Util::bootSecs() - lastPing > 30){
sendCommand("GET_PARAMETER", url.getUrl(), "");
lastPing = Util::bootSecs();
}
@ -236,7 +231,7 @@ namespace Mist{
statsPage.finish();
if (!tcpCon){return "TCP connection closed";}
if (!config->is_active){return "received deactivate signal";}
if (!nProxy.userClient.isAlive()){return "buffer shutdown";}
if (!keepAlive()){return "buffer shutdown";}
return "Unknown";
}
@ -246,8 +241,7 @@ namespace Mist{
do{
// No new data? Sleep and retry, if connection still open
if (!tcpCon.Received().size() || !tcpCon.Received().available(1)){
if (!tcpCon.spool() && tcpCon && config->is_active && nProxy.userClient.isAlive()){
nProxy.userClient.keepAlive();
if (!tcpCon.spool() && tcpCon && keepAlive()){
Util::sleep(waitTime);
if (!mustHave){return tcpCon;}
}
@ -288,14 +282,15 @@ namespace Mist{
seenSDP = true;
sdpState.parseSDP(recH.body);
recH.Clean();
INFO_MSG("SDP contained %llu tracks", myMeta.tracks.size());
INFO_MSG("SDP contained %zu tracks", M.getValidTracks().size());
return true;
}
if (recH.hasHeader("Transport")){
INFO_MSG("Received setup response");
uint32_t trackNo = sdpState.parseSetup(recH, url.host, "");
if (trackNo){
INFO_MSG("Parsed transport for track: %lu", trackNo);
recH.url = lastRequestedSetup;
size_t trackNo = sdpState.parseSetup(recH, url.host, "");
if (trackNo != INVALID_TRACK_ID){
INFO_MSG("Parsed transport for track: %zu", trackNo);
transportSet = true;
}else{
INFO_MSG("Could not parse transport string!");
@ -319,17 +314,11 @@ namespace Mist{
recH.Clean();
return true;
}
if (!tcpCon.spool() && tcpCon && config->is_active && nProxy.userClient.isAlive()){
nProxy.userClient.keepAlive();
Util::sleep(waitTime);
}
if (!tcpCon.spool() && tcpCon && keepAlive()){Util::sleep(waitTime);}
continue;
}
if (!tcpCon.Received().available(4)){
if (!tcpCon.spool() && tcpCon && config->is_active && nProxy.userClient.isAlive()){
nProxy.userClient.keepAlive();
Util::sleep(waitTime);
}
if (!tcpCon.spool() && tcpCon && keepAlive()){Util::sleep(waitTime);}
continue;
}// a TCP RTP packet, but not complete yet
@ -345,19 +334,26 @@ namespace Mist{
std::string tcpPacket = tcpCon.Received().remove(len + 4);
RTP::Packet pkt(tcpPacket.data() + 4, len);
uint8_t chan = tcpHead.data()[1];
uint32_t trackNo = sdpState.getTrackNoForChannel(chan);
EXTREME_MSG("Received %ub RTP packet #%u on channel %u, time %llu", len,
(unsigned int)pkt.getSequence(), chan, pkt.getTimeStamp());
if (!trackNo && (chan % 2) != 1){
size_t trackNo = sdpState.getTrackNoForChannel(chan);
EXTREME_MSG("Received %ub RTP packet #%u on channel %u, time %" PRIu32, len,
pkt.getSequence(), chan, pkt.getTimeStamp());
if ((trackNo == INVALID_TRACK_ID) && (chan % 2) != 1){
WARN_MSG("Received packet for unknown track number on channel %u", chan);
}
if (trackNo){sdpState.tracks[trackNo].sorter.rtpSeq = pkt.getSequence();}
if (trackNo != INVALID_TRACK_ID){
sdpState.tracks[trackNo].sorter.rtpSeq = pkt.getSequence();
}
sdpState.handleIncomingRTP(trackNo, pkt);
if (trackNo != INVALID_TRACK_ID){
if (!userSelect.count(trackNo)){
userSelect[trackNo].reload(streamName, trackNo, COMM_STATUS_SOURCE | COMM_STATUS_DONOTTRACK);
}
sdpState.handleIncomingRTP(trackNo, pkt);
}
return true;
}while (tcpCon && config->is_active && nProxy.userClient.isAlive());
}while (tcpCon && keepAlive());
return false;
}
@ -365,7 +361,7 @@ namespace Mist{
bool InputRTSP::handleUDP(){
if (TCPmode){return false;}
bool r = false;
for (std::map<uint32_t, SDP::Track>::iterator it = sdpState.tracks.begin();
for (std::map<size_t, SDP::Track>::iterator it = sdpState.tracks.begin();
it != sdpState.tracks.end(); ++it){
Socket::UDPConnection &s = it->second.data;
it->second.sorter.setCallback(it->first, insertRTP);
@ -380,14 +376,29 @@ namespace Mist{
if (!it->second.theirSSRC){it->second.theirSSRC = pack.getSSRC();}
it->second.sorter.addPacket(pack);
}
if (Util::epoch() / 5 != it->second.rtcpSent){
it->second.rtcpSent = Util::epoch() / 5;
it->second.pack.sendRTCP_RR(connectedAt, it->second, it->first, myMeta, sendUDP);
if (Util::bootSecs() != it->second.rtcpSent){
it->second.rtcpSent = Util::bootSecs();
it->second.pack.sendRTCP_RR(it->second, sendUDP);
}
}
return r;
}
void InputRTSP::incoming(const DTSC::Packet &pkt){nProxy.bufferLivePacket(pkt, myMeta);}
void InputRTSP::incoming(const DTSC::Packet &pkt){
if (!M.getBootMsOffset()){
meta.setBootMsOffset(Util::bootMS() - pkt.getTime());
packetOffset = 0;
setPacketOffset = true;
}else if (!setPacketOffset){
packetOffset = (Util::bootMS() - pkt.getTime()) - M.getBootMsOffset();
setPacketOffset = true;
}
static DTSC::Packet newPkt;
char *pktData;
size_t pktDataLen;
pkt.getString("data", pktData, pktDataLen);
bufferLivePacket(pkt.getTime() + packetOffset, pkt.getInt("offset"), pkt.getTrackId(), pktData,
pktDataLen, 0, pkt.getFlag("keyframe"));
}
}// namespace Mist

View file

@ -22,11 +22,9 @@ namespace Mist{
bool checkArguments();
bool needHeader(){return false;}
bool readHeader(){return true;}
void getNext(bool smart = true){}
bool openStreamSource();
void closeStreamSource();
void parseStreamHeader();
void seek(int seekTime){}
void sendCommand(const std::string &cmd, const std::string &cUrl, const std::string &body,
const std::map<std::string, std::string> *extraHeaders = 0, bool reAuth = true);
bool parsePacket(bool mustHave = false);
@ -43,8 +41,10 @@ namespace Mist{
bool TCPmode;
bool needAuth;
std::string session;
long long connectedAt; ///< The timestamp the connection was made, as reference point for RTCP
/// packets.
bool setPacketOffset;
int64_t packetOffset;
std::string lastRequestedSetup;
};
}// namespace Mist

View file

@ -47,25 +47,23 @@ namespace Mist{
bool InputSrt::readHeader(){
if (!fileSource.good()){return false;}
myMeta.tracks[1].trackID = 1;
myMeta.tracks[1].type = "meta";
myMeta.tracks[1].codec = "subtitle";
size_t idx = meta.addTrack();
meta.setID(idx, 1);
meta.setType(idx, "meta");
meta.setCodec(idx, "subtitle");
getNext();
while (thisPacket){
myMeta.update(thisPacket);
meta.update(thisPacket);
getNext();
}
// outputting dtsh file
myMeta.toFile(config->getString("input") + ".dtsh");
M.toFile(config->getString("input") + ".dtsh");
return true;
}
void InputSrt::getNext(bool smart){
bool hasPacket = false;
void InputSrt::getNext(size_t idx){
thisPacket.null();
std::string line;
@ -93,7 +91,7 @@ namespace Mist{
static JSON::Value thisPack;
thisPack.null();
thisPack["trackid"] = 1;
thisPack["bpos"] = (uint64_t)fileSource.tellg();
thisPack["bpos"] = fileSource.tellg();
thisPack["data"] = data;
thisPack["index"] = index;
thisPack["time"] = timestamp;
@ -133,12 +131,6 @@ namespace Mist{
thisPacket.null();
}
void InputSrt::seek(int seekTime){fileSource.seekg(0, fileSource.beg);}
void InputSrt::trackSelect(std::string trackSpec){
// we only have one track..
selectedTracks.clear();
selectedTracks.insert(1);
}
void InputSrt::seek(uint64_t seekTime, size_t idx){fileSource.seekg(0, fileSource.beg);}
}// namespace Mist

View file

@ -16,9 +16,8 @@ namespace Mist{
bool checkArguments();
bool readHeader();
bool preRun();
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 vtt;
FILE *inFile;

View file

@ -27,82 +27,112 @@ TS::Stream liveStream(true);
Util::Config *cfgPointer = NULL;
#define THREAD_TIMEOUT 15
std::map<unsigned long long, unsigned long long> threadTimer;
std::map<size_t, uint64_t> threadTimer;
std::set<unsigned long> claimableThreads;
std::set<size_t> claimableThreads;
void parseThread(void *ignored){
void parseThread(void *mistIn){
Mist::inputTS *input = reinterpret_cast<Mist::inputTS *>(mistIn);
int tid = -1;
size_t tid = 0;
{
tthread::lock_guard<tthread::mutex> guard(threadClaimMutex);
if (claimableThreads.size()){
tid = *claimableThreads.begin();
claimableThreads.erase(claimableThreads.begin());
}
if (tid == -1){return;}
}
if (tid == 0){return;}
Mist::negotiationProxy myProxy;
myProxy.streamName = globalStreamName;
DTSC::Meta myMeta;
Comms::Users userConn;
DTSC::Meta meta;
if (liveStream.isDataTrack(tid)){
bool dataTrack = liveStream.isDataTrack(tid);
if (dataTrack){
if (!Util::streamAlive(globalStreamName) &&
!Util::startInput(globalStreamName, "push://INTERNAL_ONLY:" + cfgPointer->getString("input"), true, true)){
FAIL_MSG("Could not start buffer for %s", globalStreamName.c_str());
return;
}
char userPageName[NAME_BUFFER_SIZE];
snprintf(userPageName, NAME_BUFFER_SIZE, SHM_USERS, globalStreamName.c_str());
myProxy.userClient = IPC::sharedClient(userPageName, PLAY_EX_SIZE, true);
myProxy.userClient.countAsViewer = false;
{
tthread::lock_guard<tthread::mutex> guard(threadClaimMutex);
if (!input->hasMeta()){input->reloadClientMeta();}
}
meta.reInit(globalStreamName, false);
}
size_t idx = meta.trackIDToIndex(tid, getpid());
threadTimer[tid] = Util::bootSecs();
while (Util::bootSecs() - threadTimer[tid] < THREAD_TIMEOUT && cfgPointer->is_active &&
(!liveStream.isDataTrack(tid) || myProxy.userClient.isAlive())){
(!liveStream.isDataTrack(tid) || (userConn ? userConn.isAlive() : true))){
{
tthread::lock_guard<tthread::mutex> guard(threadClaimMutex);
threadTimer[tid] = Util::bootSecs();
}
if (liveStream.isDataTrack(tid)){myProxy.userClient.keepAlive();}
if (liveStream.isDataTrack(tid)){userConn.keepAlive();}
liveStream.parse(tid);
if (!liveStream.hasPacket(tid)){
Util::sleep(100);
continue;
}
uint64_t startSecs = Util::bootSecs();
while (liveStream.hasPacket(tid) && ((Util::bootSecs() < startSecs + 2) && cfgPointer->is_active &&
(!liveStream.isDataTrack(tid) || myProxy.userClient.isAlive()))){
liveStream.initializeMetadata(myMeta, tid);
DTSC::Packet pack;
liveStream.getPacket(tid, pack);
if (!pack){
Util::sleep(100);
break;
while (liveStream.hasPacket(tid) &&
((Util::bootSecs() < startSecs + 2) && cfgPointer->is_active &&
(!liveStream.isDataTrack(tid) || (userConn ? userConn.isAlive() : true)))){
liveStream.parse(tid);
if (liveStream.hasPacket(tid)){
if (idx == INVALID_TRACK_ID){
tthread::lock_guard<tthread::mutex> guard(threadClaimMutex);
liveStream.initializeMetadata(meta, tid);
idx = meta.trackIDToIndex(tid, getpid());
if (idx != INVALID_TRACK_ID){
userConn.reload(globalStreamName, idx, COMM_STATUS_SOURCE | COMM_STATUS_DONOTTRACK);
input->reloadClientMeta();
}
}
if (idx == INVALID_TRACK_ID || !meta.trackValid(idx)){continue;}
if (!meta.trackLoaded(idx)){meta.refresh();}
DTSC::Packet pack;
liveStream.getPacket(tid, pack);
if (pack){
tthread::lock_guard<tthread::mutex> guard(threadClaimMutex);
if (!input->hasMeta()){input->reloadClientMeta();}
if (dataTrack){
char *data;
size_t dataLen;
pack.getString("data", data, dataLen);
input->bufferLivePacket(pack.getTime(), pack.getInt("offset"), idx, data, dataLen,
pack.getInt("bpos"), pack.getFlag("keyframe"));
}
}
}
if (myMeta.tracks.count(tid)){
myProxy.continueNegotiate(tid, myMeta, true);
myProxy.bufferLivePacket(pack, myMeta);
{
tthread::lock_guard<tthread::mutex> guard(threadClaimMutex);
threadTimer[tid] = Util::bootSecs();
}
if (!liveStream.hasPacket(tid)){
if (liveStream.isDataTrack(tid)){userConn.keepAlive();}
Util::sleep(100);
}
}
}
std::string reason = "unknown reason";
if (!(Util::bootSecs() - threadTimer[tid] < THREAD_TIMEOUT)){reason = "thread timeout";}
if (!cfgPointer->is_active){reason = "input shutting down";}
if (!(!liveStream.isDataTrack(tid) || myProxy.userClient.isAlive())){
if (!(!liveStream.isDataTrack(tid) || userConn.isAlive())){
reason = "buffer disconnect";
cfgPointer->is_active = false;
}
INFO_MSG("Shutting down thread for %d because %s", tid, reason.c_str());
INFO_MSG("Shutting down thread for %zu because %s", tid, reason.c_str());
{
tthread::lock_guard<tthread::mutex> guard(threadClaimMutex);
threadTimer.erase(tid);
}
liveStream.eraseTrack(tid);
myProxy.userClient.finish();
if (dataTrack && userConn){userConn.setStatus(COMM_STATUS_DISCONNECT);}
}
namespace Mist{
@ -117,6 +147,7 @@ namespace Mist{
"standard input (ts-exec:*), or multicast/unicast UDP sockets (tsudp://*).";
capa["source_match"].append("/*.ts");
capa["source_file"] = "$source";
capa["source_match"].append("/*.m2ts");
capa["source_match"].append("stream://*.ts");
capa["source_match"].append("tsudp://*");
capa["source_match"].append("ts-exec:*");
@ -143,9 +174,9 @@ namespace Mist{
capa["codecs"][0u][1u].append("MP2");
inFile = NULL;
inputProcess = 0;
isFinished = false;
{
int fin = 0, fout = 0, ferr = 0;
pid_t srt_tx = -1;
const char *args[] ={"srt-live-transmit", 0};
srt_tx = Util::Procs::StartPiped(args, 0, 0, 0);
@ -265,23 +296,6 @@ namespace Mist{
return inFile;
}
/// Track selector of TS Input
///\arg trackSpec specifies which tracks are to be selected
///\todo test whether selecting a subset of tracks work
void inputTS::trackSelect(std::string trackSpec){
selectedTracks.clear();
long long 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);
}else{
trackSpec = "";
}
}
}
bool inputTS::needHeader(){
if (!standAlone){return false;}
return Input::needHeader();
@ -295,6 +309,7 @@ namespace Mist{
///\todo Find errors, perhaps parts can be made more modular
bool inputTS::readHeader(){
if (!inFile){return false;}
meta.reInit(streamName);
TS::Packet packet; // to analyse and extract data
DTSC::Packet headerPack;
fseek(inFile, 0, SEEK_SET); // seek to beginning
@ -306,28 +321,39 @@ namespace Mist{
if (packet.getUnitStart()){
while (tsStream.hasPacketOnEachTrack()){
tsStream.getEarliestPacket(headerPack);
if (!headerPack){break;}
if (!myMeta.tracks.count(headerPack.getTrackId()) ||
!myMeta.tracks[headerPack.getTrackId()].codec.size()){
tsStream.initializeMetadata(myMeta, headerPack.getTrackId());
size_t pid = headerPack.getTrackId();
size_t idx = M.trackIDToIndex(pid, getpid());
if (idx == INVALID_TRACK_ID || !M.getCodec(idx).size()){
tsStream.initializeMetadata(meta, pid);
idx = M.trackIDToIndex(pid, getpid());
}
myMeta.update(headerPack);
char *data;
size_t dataLen;
headerPack.getString("data", data, dataLen);
meta.update(headerPack.getTime(), headerPack.getInt("offset"), idx, dataLen,
headerPack.getInt("bpos"), headerPack.getFlag("keyframe"), headerPack.getDataLen());
}
}
}
tsStream.finish();
INFO_MSG("Reached %s at %llu bytes", feof(inFile) ? "EOF" : "error", lastBpos);
INFO_MSG("Reached %s at %" PRIu64 " bytes", feof(inFile) ? "EOF" : "error", lastBpos);
while (tsStream.hasPacket()){
tsStream.getEarliestPacket(headerPack);
if (!myMeta.tracks.count(headerPack.getTrackId()) ||
!myMeta.tracks[headerPack.getTrackId()].codec.size()){
tsStream.initializeMetadata(myMeta, headerPack.getTrackId());
size_t pid = headerPack.getTrackId();
size_t idx = M.trackIDToIndex(pid, getpid());
if (idx == INVALID_TRACK_ID || !M.getCodec(idx).size()){
tsStream.initializeMetadata(meta, pid);
idx = M.trackIDToIndex(pid, getpid());
}
myMeta.update(headerPack);
char *data;
size_t dataLen;
headerPack.getString("data", data, dataLen);
meta.update(headerPack.getTime(), headerPack.getInt("offset"), idx, dataLen,
headerPack.getInt("bpos"), headerPack.getFlag("keyframe"), headerPack.getDataLen());
}
fseek(inFile, 0, SEEK_SET);
myMeta.toFile(config->getString("input") + ".dtsh");
meta.toFile(config->getString("input") + ".dtsh");
return true;
}
@ -335,40 +361,42 @@ namespace Mist{
/// At the moment, the logic of sending the last packet that was finished has been implemented,
/// but the seeking and finding data is not yet ready.
///\todo Finish the implementation
void inputTS::getNext(bool smart){
INSANE_MSG("Getting next");
void inputTS::getNext(size_t idx){
size_t pid = (idx == INVALID_TRACK_ID ? 0 : M.getID(idx));
INSANE_MSG("Getting next on track %zu", idx);
thisPacket.null();
bool hasPacket =
(selectedTracks.size() == 1 ? tsStream.hasPacket(*selectedTracks.begin()) : tsStream.hasPacket());
bool hasPacket = (idx == INVALID_TRACK_ID ? tsStream.hasPacket() : tsStream.hasPacket(pid));
while (!hasPacket && !feof(inFile) &&
(inputProcess == 0 || Util::Procs::childRunning(inputProcess)) && config->is_active){
tsBuf.FromFile(inFile);
if (selectedTracks.count(tsBuf.getPID())){
if (idx == INVALID_TRACK_ID || pid == tsBuf.getPID()){
tsStream.parse(tsBuf, 0); // bPos == 0
if (tsBuf.getUnitStart()){
hasPacket = (selectedTracks.size() == 1 ? tsStream.hasPacket(*selectedTracks.begin())
: tsStream.hasPacket());
hasPacket = (idx == INVALID_TRACK_ID ? tsStream.hasPacket() : tsStream.hasPacket(pid));
}
}
}
if (feof(inFile)){
tsStream.finish();
if (!isFinished){
tsStream.finish();
isFinished = true;
}
hasPacket = true;
}
if (!hasPacket){return;}
if (selectedTracks.size() == 1){
if (tsStream.hasPacket(*selectedTracks.begin())){
tsStream.getPacket(*selectedTracks.begin(), thisPacket);
}
}else{
if (idx == INVALID_TRACK_ID){
if (tsStream.hasPacket()){tsStream.getEarliestPacket(thisPacket);}
}else{
if (tsStream.hasPacket(pid)){tsStream.getPacket(pid, thisPacket);}
}
if (!thisPacket){
INFO_MSG("Could not getNext TS packet!");
return;
}
tsStream.initializeMetadata(myMeta);
if (!myMeta.tracks.count(thisPacket.getTrackId())){getNext();}
tsStream.initializeMetadata(meta);
size_t thisIdx = M.trackIDToIndex(thisPacket.getTrackId(), getpid());
if (thisIdx == INVALID_TRACK_ID){getNext(idx);}
}
void inputTS::readPMT(){
@ -388,24 +416,31 @@ namespace Mist{
tsStream.partialClear();
// Restore original file position
if (Util::fseek(inFile, bpos, SEEK_SET)){return;}
if (Util::fseek(inFile, bpos, SEEK_SET)){
clearerr(inFile);
return;
}
}
/// Seeks to a specific time
void inputTS::seek(int seekTime){
void inputTS::seek(uint64_t seekTime, size_t idx){
tsStream.clear();
readPMT();
uint64_t seekPos = 0xFFFFFFFFFFFFFFFFull;
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();
tsStream.setLastms(*it, keyIt->getTime());
uint64_t seekPos = 0xFFFFFFFFull;
if (idx != INVALID_TRACK_ID){
DTSC::Keys keys(M.keys(idx));
uint32_t keyNum = keys.getNumForTime(seekTime);
seekPos = keys.getBpos(keyNum);
}else{
std::set<size_t> tracks = M.getValidTracks();
for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){
DTSC::Keys keys(M.keys(*it));
uint32_t keyNum = keys.getNumForTime(seekTime);
uint64_t thisBPos = keys.getBpos(keyNum);
if (thisBPos < seekPos){seekPos = thisBPos;}
}
if (thisBPos < seekPos){seekPos = thisBPos;}
}
clearerr(inFile);
Util::fseek(inFile, seekPos, SEEK_SET); // seek to the correct position
}
@ -424,22 +459,23 @@ namespace Mist{
}
void inputTS::parseStreamHeader(){
// Placeholder to force normal code to continue despite no tracks available
myMeta.tracks[0].type = "audio";
// Placeholder empty track to force normal code to continue despite no tracks available
tmpIdx = meta.addTrack(0, 0, 0, 0);
}
std::string inputTS::streamMainLoop(){
myMeta.tracks.clear(); // wipe the placeholder track from above
IPC::sharedClient statsPage = IPC::sharedClient(SHM_STATISTICS, STAT_EX_SIZE, true);
meta.removeTrack(tmpIdx);
INFO_MSG("Removed temptrack %zu", tmpIdx);
Comms::Statistics statComm;
uint64_t downCounter = 0;
uint64_t startTime = Util::epoch();
uint64_t startTime = Util::bootSecs();
uint64_t noDataSince = Util::bootSecs();
bool gettingData = false;
bool hasStarted = false;
cfgPointer = config;
globalStreamName = streamName;
unsigned long long threadCheckTimer = Util::bootSecs();
while (config->is_active && nProxy.userClient.isAlive()){
while (config->is_active){
if (tcpCon){
if (tcpCon.spool()){
while (tcpCon.Received().available(188)){
@ -471,7 +507,7 @@ namespace Mist{
gettingData = true;
INFO_MSG("Now receiving UDP data...");
}
int offset = 0;
size_t offset = 0;
// Try to read full TS Packets
// Watch out! We push here to a global, in order for threads to be able to access it.
while (offset < udpCon.data_len){
@ -489,7 +525,7 @@ namespace Mist{
uint32_t maxBytes =
std::min((uint32_t)(188 - leftData.size()), (uint32_t)(udpCon.data_len - offset));
uint32_t numBytes = maxBytes;
VERYHIGH_MSG("%lu bytes of non-sync-byte data received", numBytes);
VERYHIGH_MSG("%" PRIu32 " bytes of non-sync-byte data received", numBytes);
if (leftData.size()){
leftData.append(udpCon.data + offset, numBytes);
while (leftData.size() >= 188){
@ -517,28 +553,23 @@ namespace Mist{
// Check for and spawn threads here.
if (Util::bootSecs() - threadCheckTimer > 1){
// Connect to stats for INPUT detection
uint64_t now = Util::epoch();
if (!statsPage.getData()){
statsPage = IPC::sharedClient(SHM_STATISTICS, STAT_EX_SIZE, true);
}
if (statsPage.getData()){
if (!statsPage.isAlive()){
statComm.reload();
if (statComm){
if (!statComm.isAlive()){
config->is_active = false;
statsPage.finish();
return "received shutdown request from controller";
}
IPC::statExchange tmpEx(statsPage.getData());
tmpEx.now(now);
tmpEx.crc(getpid());
tmpEx.streamName(streamName);
tmpEx.connector("INPUT");
tmpEx.up(0);
tmpEx.down(downCounter + tcpCon.dataDown());
tmpEx.time(now - startTime);
tmpEx.lastSecond(0);
statsPage.keepAlive();
uint64_t now = Util::bootSecs();
statComm.setNow(now);
statComm.setCRC(getpid());
statComm.setStream(streamName);
statComm.setConnector("INPUT");
statComm.setUp(0);
statComm.setDown(downCounter + tcpCon.dataDown());
statComm.setTime(now - startTime);
statComm.setLastSecond(0);
statComm.keepAlive();
}
nProxy.userClient.keepAlive();
std::set<size_t> activeTracks = liveStream.getActiveTracks();
{
@ -546,7 +577,6 @@ namespace Mist{
if (hasStarted && !threadTimer.size()){
if (!isAlwaysOn()){
config->is_active = false;
statsPage.finish();
return "no active threads and we had input in the past";
}else{
hasStarted = false;
@ -555,7 +585,8 @@ namespace Mist{
for (std::set<size_t>::iterator it = activeTracks.begin(); it != activeTracks.end(); it++){
if (!liveStream.isDataTrack(*it)){continue;}
if (threadTimer.count(*it) && ((Util::bootSecs() - threadTimer[*it]) > (2 * THREAD_TIMEOUT))){
WARN_MSG("Thread for track %d timed out %d seconds ago without a clean shutdown.",
WARN_MSG("Thread for track %" PRIu64 " timed out %" PRIu64
" seconds ago without a clean shutdown.",
*it, Util::bootSecs() - threadTimer[*it]);
threadTimer.erase(*it);
}
@ -566,7 +597,7 @@ namespace Mist{
claimableThreads.insert(*it);
// Spawn thread here.
tthread::thread thisThread(parseThread, 0);
tthread::thread thisThread(parseThread, this);
thisThread.detach();
}
}
@ -576,14 +607,12 @@ namespace Mist{
if (Util::bootSecs() - noDataSince > 20){
if (!isAlwaysOn()){
config->is_active = false;
statsPage.finish();
return "No packets received for 20 seconds - terminating";
}else{
noDataSince = Util::bootSecs();
}
}
}
statsPage.finish();
return "received shutdown request";
}
@ -612,9 +641,8 @@ namespace Mist{
inpt.substr(0, 7) != "http://" && inpt.substr(0, 10) != "http-ts://" &&
inpt.substr(0, 8) != "https://" && inpt.substr(0, 11) != "https-ts://"){
return Input::needsLock();
}else{
return false;
}
return false;
}
}// namespace Mist

View file

@ -20,9 +20,8 @@ namespace Mist{
bool preRun();
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 seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
void readPMT();
bool openStreamSource();
void parseStreamHeader();
@ -34,6 +33,8 @@ namespace Mist{
Socket::Connection tcpCon;
TS::Packet tsBuf;
pid_t inputProcess;
size_t tmpIdx;
bool isFinished;
};
}// namespace Mist