From 79b4772c3dc04d69c3537565c3222e62aa317672 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 27 Nov 2013 15:29:49 +0100 Subject: [PATCH] Metadata upgrades. --- lib/Makefile.am | 2 +- lib/dtsc.cpp | 267 +++++----------- lib/dtsc.h | 223 +++++++++++--- lib/dtscmeta.cpp | 786 +++++++++++++++++++++++++++++++++++++++++++++++ lib/flv_tag.cpp | 165 ++++------ lib/flv_tag.h | 8 +- lib/ogg.h | 8 +- 7 files changed, 1116 insertions(+), 343 deletions(-) create mode 100644 lib/dtscmeta.cpp diff --git a/lib/Makefile.am b/lib/Makefile.am index cc86a62f..aaef3331 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,7 +3,7 @@ libmist_1_0_la_SOURCES=amf.h amf.cpp libmist_1_0_la_SOURCES+=auth.h auth.cpp libmist_1_0_la_SOURCES+=base64.h base64.cpp libmist_1_0_la_SOURCES+=config.h config.cpp -libmist_1_0_la_SOURCES+=dtsc.h dtsc.cpp +libmist_1_0_la_SOURCES+=dtsc.h dtsc.cpp dtscmeta.cpp libmist_1_0_la_SOURCES+=flv_tag.h flv_tag.cpp libmist_1_0_la_SOURCES+=http_parser.h http_parser.cpp libmist_1_0_la_SOURCES+=json.h json.cpp diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 01063ff6..87a29c94 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -51,15 +51,8 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ return false; } unsigned int i = 0; - metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); - metadata.removeMember("moreheader"); - metadata.netPrepare(); - trackMapping.clear(); - if (metadata.isMember("tracks")){ - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); - } - } + JSON::Value meta = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); + metadata = Meta(meta); buffer.erase(0, len + 8); if (buffer.length() <= 8){ return false; @@ -127,17 +120,8 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ } unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); - metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); - metadata.removeMember("moreheader"); - if (buffercount > 1){ - metadata.netPrepare(); - } - if (metadata.isMember("tracks")){ - trackMapping.clear(); - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); - } - } + JSON::Value meta = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); + metadata = Meta(meta); //recursively calls itself until failure or data packet instead of header return parsePacket(buffer); } @@ -179,18 +163,13 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ /// Adds a keyframe packet to all tracks, so the stream can be fully played. void DTSC::Stream::endStream(){ - if (metadata.isMember("tracks") && metadata["tracks"].size() > 0){ - JSON::Value trackData = metadata["tracks"]; - for (JSON::ObjIter it = trackData.ObjBegin(); it != trackData.ObjEnd(); it++){ - if(it->second.isMember("lastms") && it->second.isMember("trackid")){ // TODO - JSON::Value newPack; - newPack["time"] = it->second["lastms"]; - newPack["trackid"] = it->second["trackid"]; - newPack["keyframe"] = 1ll; - newPack["data"] = ""; - addPacket(newPack); - } - } + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + JSON::Value newPack; + newPack["time"] = it->second.lastms; + newPack["trackid"] = it->second.trackID; + newPack["keyframe"] = 1ll; + newPack["data"] = ""; + addPacket(newPack); } } @@ -243,7 +222,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ }else{ resetStream(); } - std::string newTrack = trackMapping[newPos.trackID]; + std::string newTrack = metadata.tracks[newPos.trackID].getIdentifier(); while (buffers.count(newPos) > 0){ newPos.seekTime++; } @@ -253,8 +232,8 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } datapointertype = INVALID; std::string tmp = ""; - if (newPack.isMember("trackid")){ - tmp = getTrackById(newPack["trackid"].asInt())["type"].asStringRef(); + if (newPack.isMember("trackid") && newPack["trackid"].asInt() > 0){ + tmp = metadata.tracks[newPack["trackid"].asInt()].type; } if (newPack.isMember("datatype")){ tmp = newPack["datatype"].asStringRef(); @@ -268,75 +247,12 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (tmp == "meta"){ datapointertype = META; } - if (tmp == "pause_marker"){ + if (tmp == "pause_marker" || (newPack.isMember("mark") && newPack["mark"].asStringRef() == "pause")){ datapointertype = PAUSEMARK; } - int keySize = metadata["tracks"][newTrack]["keys"].size(); if (buffercount > 1){ - #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] - if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 5000 > prevKey["time"].asInt())){ - metadata["tracks"][newTrack]["lastms"] = newPack["time"]; - keyframes[newPos.trackID].insert(newPos); - JSON::Value key; - key["time"] = newPack["time"]; - if (keySize){ - key["num"] = prevKey["num"].asInt() + 1; - prevKey["len"] = newPack["time"].asInt() - prevKey["time"].asInt(); - int size = 0; - for (JSON::ArrIter it = prevKey["parts"].ArrBegin(); it != prevKey["parts"].ArrEnd(); it++){ - size += it->asInt(); - } - prevKey["partsize"] = prevKey["parts"].size(); - std::string tmpParts = JSON::encodeVector(prevKey["parts"].ArrBegin(), prevKey["parts"].ArrEnd()); - prevKey["parts"] = tmpParts; - prevKey["size"] = size; - long long int bps = (double)prevKey["size"].asInt() / ((double)prevKey["len"].asInt() / 1000.0); - if (bps > metadata["tracks"][newTrack]["maxbps"].asInt()){ - metadata["tracks"][newTrack]["maxbps"] = (long long int)(bps * 1.2); - } - }else{ - key["num"] = 1; - } - metadata["tracks"][newTrack]["keys"].append(key); - keySize = metadata["tracks"][newTrack]["keys"].size(); - - //find the last fragment - JSON::Value lastFrag; - if (metadata["tracks"][newTrack]["frags"].size() > 0){ - lastFrag = metadata["tracks"][newTrack]["frags"][metadata["tracks"][newTrack]["frags"].size() - 1]; - } - //find the first keyframe past the last fragment - JSON::ArrIter fragIt = metadata["tracks"][newTrack]["keys"].ArrBegin(); - while (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1 && (*fragIt)["num"].asInt() < lastFrag["num"].asInt() + lastFrag["len"].asInt()){ - fragIt++; - } - //continue only if a keyframe was found - if (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1){ - //calculate the variables of the new fragment - JSON::Value newFrag; - newFrag["num"] = (*fragIt)["num"]; - newFrag["time"] = (*fragIt)["time"]; - newFrag["len"] = 1ll; - newFrag["dur"] = (*fragIt)["len"]; - fragIt++; - //keep calculating until 10+ seconds or no more keyframes - while (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1){ - newFrag["len"] = newFrag["len"].asInt() + 1; - newFrag["dur"] = newFrag["dur"].asInt() + (*fragIt)["len"].asInt(); - //more than 5 seconds? store the new fragment - if (newFrag["dur"].asInt() >= 5000 || (*fragIt)["len"].asInt() < 2){ - /// \todo Make this variable instead of hardcoded 5 seconds? - metadata["tracks"][newTrack]["frags"].append(newFrag); - break; - } - fragIt++; - } - } - } - if (keySize){ - metadata["tracks"][newTrack]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asStringRef().size()); - } - metadata["live"] = 1ll; + metadata.update(newPack); + metadata.live = true; } //increase buffer size if too little time available @@ -346,8 +262,8 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffercount = buffers.size(); if (buffercount < 2){buffercount = 2;} } - if (metadata["buffer_window"].asInt() < timeBuffered){ - metadata["buffer_window"] = (long long int)timeBuffered; + if (metadata.bufferWindow < timeBuffered){ + metadata.bufferWindow = timeBuffered; } } @@ -359,23 +275,24 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ /// Deletes a the first part of the buffer, updating the keyframes list and metadata as required. /// Will print a warning to std::cerr if a track has less than 2 keyframes left because of this. void DTSC::Stream::cutOneBuffer(){ - if (buffercount > 1 && keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ + int trid = buffers.begin()->first.trackID; + if (buffercount > 1 && keyframes[trid].count(buffers.begin()->first)){ //if there are < 3 keyframes, throwing one away would mean less than 2 left. - if (keyframes[buffers.begin()->first.trackID].size() < 3){ - std::cerr << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; + if (keyframes[trid].size() < 3){ + std::cerr << "Warning - track " << trid << " doesn't have enough keyframes to be reliably served." << std::endl; } - std::string track = trackMapping[buffers.begin()->first.trackID]; - keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); - int keySize = metadata["tracks"][track]["keys"].size(); - metadata["tracks"][track]["keys"].shrink(keySize - 1); - if (metadata["tracks"][track]["frags"].size() > 0){ - // delete fragments of which the beginning can no longer be reached - while (metadata["tracks"][track]["frags"].size() > 0 && metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ - metadata["tracks"][track]["firstms"] = metadata["tracks"][track]["firstms"].asInt() + metadata["tracks"][track]["frags"][0u]["dur"].asInt(); - metadata["tracks"][track]["frags"].shrink(metadata["tracks"][track]["frags"].size() - 1); - // increase the missed fragments counter - metadata["tracks"][track]["missed_frags"] = metadata["tracks"][track]["missed_frags"].asInt() + 1; - } + keyframes[trid].erase(buffers.begin()->first); + int keySize = metadata.tracks[trid].keys.size(); + for (int i = 0; i < metadata.tracks[trid].keys[0].getParts(); i++){ + metadata.tracks[trid].parts.pop_front(); + } + metadata.tracks[trid].keys.pop_front(); + // delete fragments of which the beginning can no longer be reached + while (metadata.tracks[trid].fragments.size() && metadata.tracks[trid].fragments[0].getNumber() < metadata.tracks[trid].keys[0].getNumber()){ + metadata.tracks[trid].firstms += metadata.tracks[trid].fragments[0].getDuration(); + metadata.tracks[trid].fragments.pop_front(); + // increase the missed fragments counter + metadata.tracks[trid].missedFrags ++; } } buffers.erase(buffers.begin()); @@ -401,15 +318,6 @@ JSON::Value & DTSC::Stream::getPacket(){ return buffers.begin()->second; } -/// Returns a track element by giving the id. -JSON::Value & DTSC::Stream::getTrackById(int trackNo){ - static JSON::Value empty; - if (trackMapping.find(trackNo) != trackMapping.end()){ - return metadata["tracks"][trackMapping[trackNo]]; - } - return empty; -} - /// Returns the type of the last received packet. DTSC::datatype DTSC::Stream::lastType(){ return datapointertype; @@ -417,12 +325,22 @@ DTSC::datatype DTSC::Stream::lastType(){ /// Returns true if the current stream contains at least one video track. bool DTSC::Stream::hasVideo(){ - return metadata.isMember("video"); + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + if (it->second.type == "video"){ + return true; + } + } + return false; } /// Returns true if the current stream contains at least one audio track. bool DTSC::Stream::hasAudio(){ - return metadata.isMember("audio"); + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + if (it->second.type == "audio"){ + return true; + } + } + return false; } void DTSC::Stream::setBufferTime(unsigned int ms){ @@ -446,7 +364,7 @@ std::string & DTSC::Stream::outPacket(livePos num){ /// Returns a packed DTSC header, ready to sent over the network. std::string & DTSC::Stream::outHeader(){ - return metadata.toNetPacked(); + return metadata.toJSON().toNetPacked(); } /// Constructs a new Ring, at the given buffer position. @@ -483,16 +401,16 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ int DTSC::Stream::canSeekms(unsigned int ms){ bool too_late = false; //no tracks? Frame too new by definition. - if ( !metadata.isMember("tracks") || metadata["tracks"].size() < 1){ + if ( !metadata.tracks.size()){ return 1; } //loop trough all the tracks - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - if (it->second.isMember("keys") && it->second["keys"].size() > 0){ - if (it->second["keys"][0u]["time"].asInt() <= ms && it->second["keys"][it->second["keys"].size() - 1]["time"].asInt() >= ms){ + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + if (it->second.keys.size()){ + if (it->second.keys[0].getTime() <= ms && it->second.keys[it->second.keys.size() - 1].getTime() >= ms){ return 0; } - if (it->second["keys"][0u]["time"].asInt() > ms){too_late = true;} + if (it->second.keys[0].getTime() > ms){too_late = true;} } } //did we spot a track already past this point? return too late. @@ -505,7 +423,7 @@ DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTrack std::set seekTracks = allowedTracks; livePos result = buffers.begin()->first; for (std::set::iterator it = allowedTracks.begin(); it != allowedTracks.end(); it++){ - if (getTrackById(*it).isMember("type") && getTrackById(*it)["type"].asStringRef() == "video"){ + if (metadata.tracks[*it].type == "video"){ int trackNo = *it; seekTracks.clear(); seekTracks.insert(trackNo); @@ -616,18 +534,12 @@ DTSC::File::File(std::string filename, bool create){ headerSize = ntohl(ubuffer[0]); } readHeader(0); - trackMapping.clear(); - if (metadata.isMember("tracks")){ - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); - } - } fseek(F, 8 + headerSize, SEEK_SET); currframe = 0; } /// Returns the header metadata for this file as JSON::Value. -JSON::Value & DTSC::File::getMeta(){ +DTSC::readOnlyMeta & DTSC::File::getMeta(){ return metadata; } @@ -688,19 +600,19 @@ void DTSC::File::readHeader(int pos){ fprintf(stderr, "Could not read header (H%i)\n", pos); } strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } if (memcmp(buffer, DTSC::Magic_Header, 4) != 0){ fprintf(stderr, "Invalid header - %.4s != %.4s (H%i)\n", buffer, DTSC::Magic_Header, pos); strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read size (H%i)\n", pos); strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } uint32_t * ubuffer = (uint32_t *)buffer; @@ -710,20 +622,21 @@ void DTSC::File::readHeader(int pos){ if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet (H%i)\n", pos); strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } - metadata = JSON::fromDTMI(strbuffer); + metaStorage = JSON::fromDTMI(strbuffer); + metadata = readOnlyMeta(metaStorage);//make readonly } //if there is another header, read it and replace metadata with that one. - if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ - if (metadata["moreheader"].asInt() < getBytePosEOF()){ - readHeader(metadata["moreheader"].asInt()); + if (metadata.moreheader){ + if (metadata.moreheader < getBytePosEOF()){ + readHeader(metadata.moreheader); return; } } - metadata["vod"] = true; - metadata.netPrepare(); + metadata.vod = true; + metadata.live = false; } long int DTSC::File::getBytePosEOF(){ @@ -754,7 +667,7 @@ void DTSC::File::seekNext(){ return; } clearerr(F); - if ( !metadata.isMember("merged") || !metadata["merged"]){ + if ( !metadata.merged){ seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); } fseek(F,currentPositions.begin()->bytePos, SEEK_SET); @@ -774,7 +687,7 @@ void DTSC::File::seekNext(){ } if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){ readHeader(lastreadpos); - jsonbuffer = metadata; + jsonbuffer = metadata.toJSON(); return; } long long unsigned int version = 0; @@ -810,7 +723,7 @@ void DTSC::File::seekNext(){ }else{ jsonbuffer = JSON::fromDTMI(strbuffer); } - if (metadata.isMember("merged") && metadata["merged"]){ + if ( metadata.merged){ int tempLoc = getBytePos(); char newHeader[20]; if (fread((void*)newHeader, 20, 1, F) == 1){ @@ -823,11 +736,12 @@ void DTSC::File::seekNext(){ tmpPos.seekTime += ntohl(((int*)newHeader)[4]); }else{ tmpPos.seekTime = -1; - for (JSON::ArrIter it = getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrBegin(); it != getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrEnd(); it++){ - if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ - tmpPos.seekTime = (*it)["time"].asInt(); - tmpPos.bytePos = (*it)["bpos"].asInt(); - tmpPos.trackID = jsonbuffer["trackid"].asInt(); + long tid = jsonbuffer["trackid"].asInt(); + for (int i = 0; i != metadata.tracks[tid].keyLen; i++){ + if (metadata.tracks[tid].keys[i].getTime() > jsonbuffer["time"].asInt()){ + tmpPos.seekTime = metadata.tracks[tid].keys[i].getTime(); + tmpPos.bytePos = metadata.tracks[tid].keys[i].getBpos(); + tmpPos.trackID = tid; break; } } @@ -869,7 +783,7 @@ void DTSC::File::parseNext(){ if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){ if (lastreadpos != 0){ readHeader(lastreadpos); - jsonbuffer = metadata; + jsonbuffer = metadata.toJSON(); }else{ if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read size\n"); @@ -940,15 +854,6 @@ JSON::Value & DTSC::File::getJSON(){ return jsonbuffer; } -/// Returns a track element by giving the id. -JSON::Value & DTSC::File::getTrackById(int trackNo){ - static JSON::Value empty; - if (trackMapping.find(trackNo) != trackMapping.end()){ - return metadata["tracks"][trackMapping[trackNo]]; - } - return empty; -} - bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ seekPos tmpPos; tmpPos.trackID = trackNo; @@ -959,14 +864,13 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ tmpPos.seekTime = 0; tmpPos.bytePos = 0; } - JSON::Value & keys = metadata["tracks"][trackMapping[trackNo]]["keys"]; - for (JSON::ArrIter keyIt = keys.ArrBegin(); keyIt != keys.ArrEnd(); keyIt++){ - if ((*keyIt)["time"].asInt() > ms){ + for (int i = 0; i < metadata.tracks[trackNo].keyLen; i++){ + if (metadata.tracks[trackNo].keys[i].getTime() > ms){ break; } - if ((*keyIt)["time"].asInt() > tmpPos.seekTime){ - tmpPos.seekTime = (*keyIt)["time"].asInt(); - tmpPos.bytePos = (*keyIt)["bpos"].asInt(); + if (metadata.tracks[trackNo].keys[i].getTime() > tmpPos.seekTime){ + tmpPos.seekTime = metadata.tracks[trackNo].keys[i].getTime(); + tmpPos.bytePos = metadata.tracks[trackNo].keys[i].getBpos(); } } bool foundPacket = false; @@ -999,7 +903,6 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ } } currentPositions.insert(tmpPos); - //fprintf(stderr,"Seek_time to %d on track %d, time %d on track %d found\n", ms, trackNo, tmpPos.seekTime,tmpPos.trackID); } /// Attempts to seek to the given time in ms within the file. @@ -1037,10 +940,10 @@ bool DTSC::File::atKeyframe(){ return true; } long long int bTime = jsonbuffer["time"].asInt(); - JSON::Value & keys = getTrackById(jsonbuffer["trackid"].asInt())["keys"]; - for (JSON::ArrIter aIt = keys.ArrBegin(); aIt != keys.ArrEnd(); ++aIt){ - if ((*aIt)["time"].asInt() >= bTime){ - return ((*aIt)["time"].asInt() == bTime); + int trackid = jsonbuffer["trackid"].asInt(); + for (int i = 0; i < metadata.tracks[trackid].keyLen; i++){ + if (metadata.tracks[trackid].keys[i].getTime() >= bTime){ + return (metadata.tracks[trackid].keys[i].getTime() == bTime); } } return false; diff --git a/lib/dtsc.h b/lib/dtsc.h index 28fe8993..a3119105 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -53,53 +53,6 @@ namespace DTSC { unsigned int trackID; }; - /// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it. - class File{ - public: - File(); - File(const File & rhs); - File(std::string filename, bool create = false); - File & operator = (const File & rhs); - operator bool() const; - ~File(); - JSON::Value & getMeta(); - long long int getLastReadPos(); - bool writeHeader(std::string & header, bool force = false); - long long int addHeader(std::string & header); - long int getBytePosEOF(); - long int getBytePos(); - bool reachedEOF(); - void seekNext(); - void parseNext(); - std::string & getPacket(); - JSON::Value & getJSON(); - JSON::Value & getTrackById(int trackNo); - bool seek_time(int seconds); - bool seek_time(int seconds, int trackNo, bool forceSeek = false); - bool seek_bpos(int bpos); - void writePacket(std::string & newPacket); - void writePacket(JSON::Value & newPacket); - bool atKeyframe(); - void selectTracks(std::set & tracks); - private: - long int endPos; - void readHeader(int pos); - std::string strbuffer; - JSON::Value jsonbuffer; - JSON::Value metadata; - std::map trackMapping; - long long int currtime; - long long int lastreadpos; - int currframe; - FILE * F; - unsigned long headerSize; - char buffer[4]; - bool created; - std::set currentPositions; - std::set selectedTracks; - }; - //FileWriter - /// A simple structure used for ordering byte seek positions. struct livePos { livePos(){ @@ -149,6 +102,179 @@ namespace DTSC { volatile int playCount; }; + class Part{ + public: + short getSize(); + void setSize(short newSize); + short getDuration(); + void setDuration(short newDuration); + long getOffset(); + void setOffset(long newOffset); + char* getData(); + private: + char data[8]; + }; + + class Key{ + public: + long long unsigned int getBpos(); + void setBpos(long long unsigned int newBpos); + long getLength(); + void setLength(long newLength); + short getNumber(); + void setNumber(short newNumber); + short getParts(); + void setParts(short newParts); + long getTime(); + void setTime(long newTime); + char* getData(); + private: + char data[16]; + }; + + class Fragment{ + public: + long getDuration(); + void setDuration(long newDuration); + char getLength(); + void setLength(char newLength); + short getNumber(); + void setNumber(short newNumber); + long getSize(); + void setSize(long newSize); + char* getData(); + private: + char data[11]; + }; + + class readOnlyTrack{ + public: + readOnlyTrack(); + readOnlyTrack(JSON::Value & trackRef); + int getSendLen(); + void send(Socket::Connection & conn); + std::string getIdentifier(); + JSON::Value toJSON(); + long long unsigned int fragLen; + Fragment* fragments; + long long unsigned int keyLen; + Key* keys; + long long unsigned int partLen; + Part* parts; + int trackID; + int length; + int firstms; + int lastms; + int bps; + int missedFrags; + std::string init; + std::string codec; + std::string type; + //audio only + int rate; + int size; + int channels; + //video only + int width; + int height; + int fpks; + }; + + class Track : public readOnlyTrack { + public: + Track(); + Track(const readOnlyTrack & rhs); + Track(JSON::Value & trackRef); + inline operator bool() const {return parts.size();} + void update(JSON::Value & pack); + int getSendLen(); + void send(Socket::Connection & conn); + JSON::Value toJSON(); + std::deque fragments; + std::deque keys; + std::deque parts; + Key & getKey(int keyNum); + std::string getIdentifier(); + void reset(); + }; + + class readOnlyMeta { + public: + readOnlyMeta(); + readOnlyMeta(JSON::Value & meta); + std::map tracks; + bool vod; + bool live; + bool merged; + long long int moreheader; + long long int length; + long long int bufferWindow; + void send(Socket::Connection & conn); + JSON::Value toJSON(); + bool isFixed(); + }; + + class Meta : public readOnlyMeta { + public: + Meta(); + Meta(const readOnlyMeta & meta); + Meta(JSON::Value & meta); + inline operator bool() const {return vod || live;} + std::map tracks; + void update(JSON::Value & pack); + void send(Socket::Connection & conn); + JSON::Value toJSON(); + void reset(); + bool isFixed(); + }; + + /// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it. + class File{ + public: + File(); + File(const File & rhs); + File(std::string filename, bool create = false); + File & operator = (const File & rhs); + operator bool() const; + ~File(); + readOnlyMeta & getMeta(); + long long int getLastReadPos(); + bool writeHeader(std::string & header, bool force = false); + long long int addHeader(std::string & header); + long int getBytePosEOF(); + long int getBytePos(); + bool reachedEOF(); + void seekNext(); + void parseNext(); + std::string & getPacket(); + JSON::Value & getJSON(); + bool seek_time(int seconds); + bool seek_time(int seconds, int trackNo, bool forceSeek = false); + bool seek_bpos(int bpos); + void writePacket(std::string & newPacket); + void writePacket(JSON::Value & newPacket); + bool atKeyframe(); + void selectTracks(std::set & tracks); + private: + long int endPos; + void readHeader(int pos); + std::string strbuffer; + JSON::Value jsonbuffer; + JSON::Value metaStorage; + readOnlyMeta metadata; + std::map trackMapping; + long long int currtime; + long long int lastreadpos; + int currframe; + FILE * F; + unsigned long headerSize; + char buffer[4]; + bool created; + std::set currentPositions; + std::set selectedTracks; + }; + //FileWriter + /// Holds temporary data for a DTSC stream and provides functions to utilize it. /// Optionally also acts as a ring buffer of a certain requested size. /// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe. @@ -157,10 +283,9 @@ namespace DTSC { Stream(); ~Stream(); Stream(unsigned int buffers, unsigned int bufferTime = 0); - JSON::Value metadata; + Meta metadata; JSON::Value & getPacket(); JSON::Value & getPacket(livePos num); - JSON::Value & getTrackById(int trackNo); datatype lastType(); std::string & lastData(); bool hasVideo(); diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp new file mode 100644 index 00000000..1172e037 --- /dev/null +++ b/lib/dtscmeta.cpp @@ -0,0 +1,786 @@ +#include "dtsc.h" + +namespace DTSC { + short Part::getSize(){ + return ntohs(((short*)data)[0]); + } + + void Part::setSize(short newSize){ + ((short*)data)[0] = htons(newSize); + } + + short Part::getDuration(){ + return ntohs(((short*)(data+2))[0]); + } + + void Part::setDuration(short newDuration){ + ((short*)(data+2))[0] = htons(newDuration); + } + + long Part::getOffset(){ + return ntohl(((int*)(data+4))[0]); + } + + void Part::setOffset(long newOffset){ + ((int*)(data+4))[0] = htonl(newOffset); + } + + char* Part::getData(){ + return data; + } + + long long unsigned int Key::getBpos(){ + return (((long long unsigned int)data[0] << 32) | (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]); + } + + void Key::setBpos(long long unsigned int newBpos){ + data[4] = newBpos & 0xFF; + data[3] = (newBpos >> 8) & 0xFF; + data[2] = (newBpos >> 16) & 0xFF; + data[1] = (newBpos >> 24) & 0xFF; + data[0] = (newBpos >> 32) & 0xFF; + } + + long Key::getLength(){ + return ((data[5] << 16) | (data[6] << 8) | data[7]); + } + + void Key::setLength(long newLength){ + data[7] = newLength & 0xFF; + data[6] = (newLength >> 8) & 0xFF; + data[5] = (newLength >> 16) & 0xFF; + } + + short Key::getNumber(){ + return ntohs(((short*)(data+8))[0]); + } + + void Key::setNumber(short newNumber){ + ((short*)(data+8))[0] = htons(newNumber); + } + + short Key::getParts(){ + return ntohs(((short*)(data+10))[0]); + } + + void Key::setParts(short newParts){ + ((short*)(data+10))[0] = htons(newParts); + } + + long Key::getTime(){ + return ntohl(((int*)(data+12))[0]); + } + + void Key::setTime(long newTime){ + ((int*)(data+12))[0] = htonl(newTime); + } + + char* Key::getData(){ + return data; + } + + long Fragment::getDuration(){ + return ntohl(((int*)data)[0]); + } + + void Fragment::setDuration(long newDuration){ + ((int*)data)[0] = htonl(newDuration); + } + + char Fragment::getLength(){ + return data[4]; + } + + void Fragment::setLength(char newLength){ + data[4] = newLength; + } + + short Fragment::getNumber(){ + return ntohs(((short*)(data+5))[0]); + } + + void Fragment::setNumber(short newNumber){ + ((short*)(data+5))[0] = htons(newNumber); + } + + long Fragment::getSize(){ + return ntohl(((int*)(data+7))[0]); + } + + void Fragment::setSize(long newSize){ + ((int*)(data+7))[0] = htonl(newSize); + } + + char* Fragment::getData(){ + return data; + } + + readOnlyTrack::readOnlyTrack(){ + fragments = NULL; + fragLen = 0; + keys = NULL; + keyLen = 0; + parts = NULL; + partLen = 0; + missedFrags = 0; + } + + readOnlyTrack::readOnlyTrack(JSON::Value & trackRef){ + if (trackRef.isMember("fragments")){ + fragments = (Fragment*)trackRef["fragments"].asString().data(); + fragLen = trackRef["fragments"].asString().size() / 11; + }else{ + fragments = 0; + fragLen = 0; + } + if (trackRef.isMember("keys")){ + keys = (Key*)trackRef["keys"].asString().data(); + keyLen = trackRef["keys"].asString().size() / 16; + }else{ + keys = 0; + keyLen = 0; + } + if (trackRef.isMember("parts")){ + parts = (Part*)trackRef["parts"].asString().data(); + partLen = trackRef["parts"].asString().size() / 8; + }else{ + parts = 0; + partLen = 0; + } + trackID = trackRef["trackid"].asInt(); + length = trackRef["length"].asInt(); + firstms = trackRef["firstms"].asInt(); + lastms = trackRef["lastms"].asInt(); + bps = trackRef["bps"].asInt(); + missedFrags = trackRef["missed_fags"].asInt(); + codec = trackRef["codec"].asString(); + type = trackRef["type"].asString(); + init = trackRef["init"].asString(); + if (type == "audio"){ + rate = trackRef["rate"].asInt(); + size = trackRef["size"].asInt(); + channels = trackRef["channels"].asInt(); + } + if (type == "video"){ + width = trackRef["width"].asInt(); + height = trackRef["height"].asInt(); + fpks = trackRef["fpks"].asInt(); + } + } + + Track::Track(){} + + Track::Track(const readOnlyTrack & rhs){ + trackID = rhs.trackID; + length = rhs.length; + firstms = rhs.firstms; + lastms = rhs.lastms; + bps = rhs.bps; + missedFrags = rhs.missedFrags; + init = rhs.init; + codec = rhs.codec; + type = rhs.type; + rate = rhs.rate; + size = rhs.size; + channels = rhs.channels; + width = rhs.width; + height = rhs.height; + fpks = rhs.fpks; + if (rhs.fragments && rhs.fragLen){ + fragments = std::deque(rhs.fragments, rhs.fragments + rhs.fragLen); + } + if (rhs.keys && rhs.keyLen){ + keys = std::deque(rhs.keys, rhs.keys + rhs.keyLen); + } + if (rhs.parts && rhs.partLen){ + parts = std::deque(rhs.parts, rhs.parts + rhs.partLen); + } + } + + Track::Track(JSON::Value & trackRef){ + if (trackRef.isMember("fragments") && trackRef["fragments"].isString()){ + Fragment* tmp = (Fragment*)trackRef["fragments"].asString().data(); + fragments = std::deque(tmp,tmp + (trackRef["fragments"].asString().size() / 11)); + } + if (trackRef.isMember("keys") && trackRef["keys"].isString()){ + Key* tmp = (Key*)trackRef["keys"].asString().data(); + keys = std::deque(tmp,tmp + (trackRef["keys"].asString().size() / 16)); + } + if (trackRef.isMember("parts") && trackRef["parts"].isString()){ + Part* tmp = (Part*)trackRef["parts"].asString().data(); + parts = std::deque(tmp,tmp + (trackRef["parts"].asString().size() / 8)); + } + trackID = trackRef["trackid"].asInt(); + length = trackRef["length"].asInt(); + firstms = trackRef["firstms"].asInt(); + lastms = trackRef["lastms"].asInt(); + bps = trackRef["bps"].asInt(); + missedFrags = trackRef["missed_fags"].asInt(); + codec = trackRef["codec"].asString(); + type = trackRef["type"].asString(); + init = trackRef["init"].asString(); + if (type == "audio"){ + rate = trackRef["rate"].asInt(); + size = trackRef["size"].asInt(); + channels = trackRef["channels"].asInt(); + } + if (type == "video"){ + width = trackRef["width"].asInt(); + height = trackRef["height"].asInt(); + fpks = trackRef["fpks"].asInt(); + } + } + + void Track::update(JSON::Value & pack){ + Part newPart; + newPart.setSize(pack["data"].asString().size()); + newPart.setOffset(pack["offset"].asInt()); + if (parts.size()){ + parts[parts.size()-1].setDuration(pack["time"].asInt() - lastms); + newPart.setDuration(pack["time"].asInt() - lastms); + }else{ + newPart.setDuration(0); + } + if (pack["time"].asInt() > firstms){ + firstms = pack["time"].asInt(); + } + parts.push_back(newPart); + lastms = pack["time"].asInt(); + if (pack.isMember("keyframe") || !keys.size() || (type != "video" && pack["time"].asInt() - 2000 > keys[keys.size() - 1].getTime())){ + Key newKey; + newKey.setTime(pack["time"].asInt()); + newKey.setParts(0); + newKey.setLength(pack["time"].asInt()); + if (keys.size()){ + newKey.setNumber(keys[keys.size() - 1].getNumber() + 1); + keys[keys.size() - 1].setLength(pack["time"].asInt() - keys[keys.size() - 1].getLength()); + }else{ + newKey.setNumber(1); + } + if (pack.isMember("bpos")){//For VoD + newKey.setBpos(pack["bpos"].asInt()); + }else{ + newKey.setBpos(0); + } + keys.push_back(newKey); + if (!fragments.size() || pack["time"].asInt() - 10000 >= getKey(fragments.rbegin()->getNumber()).getTime()){ + //new fragment + Fragment newFrag; + newFrag.setDuration(0); + newFrag.setLength(1); + newFrag.setNumber(keys[keys.size() - 1].getNumber()); + if (fragments.size()){ + fragments[fragments.size() - 1].setDuration(pack["time"].asInt() - fragments[fragments.size() - 1].getDuration()); + } + newFrag.setDuration(pack["time"].asInt()); + newFrag.setSize(0); + fragments.push_back(newFrag); + }else{ + Fragment & lastFrag = fragments[fragments.size() - 1]; + lastFrag.setLength(lastFrag.getLength() + 1); + } + } + keys.rbegin()->setParts(keys.rbegin()->getParts() + 1); + fragments.rbegin()->setSize(fragments.rbegin()->getSize() + pack["data"].asString().size()); + bps += pack["data"].asString().size(); + } + + Key & Track::getKey(int keyNum){ + static Key empty; + if (keyNum < keys[0].getNumber()){ + return empty; + } + if ((keyNum - keys[0].getNumber()) > keys.size()){ + return empty; + } + return keys[keyNum - keys[0].getNumber()]; + } + + std::string readOnlyTrack::getIdentifier(){ + std::stringstream result; + if (type == ""){ + result << "Metadata_" << trackID; + return result.str(); + } + result << type << "_"; + result << codec << "_"; + result << (bps / 1024) << "kbit_"; + if (type == "audio"){ + result << channels << "ch_"; + result << rate << "hz"; + }else if (type == "video"){ + result << width << "x" << height << "_"; + result << (double)fpks / 1000 << "fps"; + } + return result.str(); + } + + std::string Track::getIdentifier(){ + std::stringstream result; + if (type == ""){ + result << "Metadata_" << trackID; + return result.str(); + } + result << type << "_"; + result << codec << "_"; + result << (bps / 1024) << "kbit_"; + if (type == "audio"){ + result << channels << "ch_"; + result << rate << "hz"; + }else if (type == "video"){ + result << width << "x" << height << "_"; + result << (double)fpks / 1000 << "fps"; + } + return result.str(); + } + + void Track::reset(){ + fragments.clear(); + parts.clear(); + keys.clear(); + bps = 0; + firstms = 999; + lastms = 0; + } + + readOnlyMeta::readOnlyMeta(){ + vod = false; + live = false; + moreheader = 0; + length = 0; + merged = false; + bufferWindow = 0; + } + + readOnlyMeta::readOnlyMeta(JSON::Value & meta){ + vod = meta.isMember("vod") && meta["vod"]; + live = meta.isMember("live") && meta["live"]; + length = 0; + merged = meta.isMember("merged") && meta["merged"]; + bufferWindow = 0; + if (meta.isMember("buffer_window")){ + bufferWindow = meta["buffer_window"].asInt(); + } + for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++){ + if (it->second.isMember("trackid") && it->second["trackid"]){ + tracks[it->second["trackid"].asInt()] = readOnlyTrack(it->second); + if (length < (it->second["lastms"].asInt() - it->second["firstms"].asInt())){ + length = (it->second["lastms"].asInt() - it->second["firstms"].asInt()); + } + } + } + moreheader = meta["moreheader"].asInt(); + } + + Meta::Meta(){ + vod = false; + live = false; + moreheader = 0; + length = 0; + merged = false; + bufferWindow = 0; + } + + Meta::Meta(const readOnlyMeta & rhs){ + vod = rhs.vod; + live = rhs.live; + merged = rhs.merged; + length = rhs.length; + bufferWindow = rhs.bufferWindow; + for (std::map::const_iterator it = rhs.tracks.begin(); it != rhs.tracks.end(); it++){ + tracks[it->first] = it->second; + } + moreheader = rhs.moreheader; + } + + Meta::Meta(JSON::Value & meta){ + vod = meta.isMember("vod") && meta["vod"]; + live = meta.isMember("live") && meta["live"]; + merged = meta.isMember("merged") && meta["merged"]; + bufferWindow = 0; + if (meta.isMember("buffer_window")){ + bufferWindow = meta["buffer_window"].asInt(); + } + for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++){ + if(it->second["trackid"].asInt()){ + tracks[it->second["trackid"].asInt()] = Track(it->second); + if (length < (it->second["lastms"].asInt() - it->second["firstms"].asInt())){ + length = (it->second["lastms"].asInt() - it->second["firstms"].asInt()); + } + } + } + moreheader = meta["moreheader"].asInt(); + } + + void Meta::update(JSON::Value & pack){ + vod = pack.isMember("bpos"); + live = !vod; + if (pack["trackid"].asInt()){ + tracks[pack["trackid"].asInt()].update(pack); + } + } + + char * convertShort(short input){ + static char result[2]; + result[0] = (input >> 8) & 0xFF; + result[1] = (input) & 0xFF; + return result; + } + + char * convertInt(int input){ + static char result[4]; + result[0] = (input >> 24) & 0xFF; + result[1] = (input >> 16) & 0xFF; + result[2] = (input >> 8) & 0xFF; + result[3] = (input) & 0xFF; + return result; + } + + char * convertLongLong(long long int input){ + static char result[8]; + result[0] = (input >> 56) & 0xFF; + result[1] = (input >> 48) & 0xFF; + result[2] = (input >> 40) & 0xFF; + result[3] = (input >> 32) & 0xFF; + result[4] = (input >> 24) & 0xFF; + result[5] = (input >> 16) & 0xFF; + result[6] = (input >> 8) & 0xFF; + result[7] = (input) & 0xFF; + return result; + } + + int readOnlyTrack::getSendLen(){ + int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); + result += fragLen * 11; + result += keyLen * 16; + result += partLen * 8; + if (type == "audio"){ + result += 49; + }else if (type == "video"){ + result += 48; + } + return result; + } + + int Track::getSendLen(){ + int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); + result += fragments.size() * 11; + result += keys.size() * 16; + result += parts.size() * 8; + if (type == "audio"){ + result += 49; + }else if (type == "video"){ + result += 48; + } + return result; + } + + void readOnlyTrack::send(Socket::Connection & conn){ + conn.SendNow(convertShort(getIdentifier().size()), 2); + conn.SendNow(getIdentifier()); + conn.SendNow("\340", 1);//Begin track object + conn.SendNow("\000\011fragments\002", 12); + conn.SendNow(convertInt(fragLen*11), 4); + conn.SendNow((char*)fragments, fragLen*11); + conn.SendNow("\000\004keys\002", 7); + conn.SendNow(convertInt(keyLen*16), 4); + conn.SendNow((char*)keys, keyLen*16); + conn.SendNow("\000\005parts\002", 8); + conn.SendNow(convertInt(partLen*8), 4); + conn.SendNow((char*)parts, partLen*8); + conn.SendNow("\000\007trackid\001", 10); + conn.SendNow(convertLongLong(trackID), 8); + conn.SendNow("\000\006length\001", 9); + conn.SendNow(convertLongLong(length), 8); + conn.SendNow("\000\007firstms\001", 10); + conn.SendNow(convertLongLong(firstms), 8); + conn.SendNow("\000\006lastms\001", 9); + conn.SendNow(convertLongLong(lastms), 8); + conn.SendNow("\000\003bps\001", 6); + conn.SendNow(convertLongLong(bps), 8); + conn.SendNow("\000\004init\002", 7); + conn.SendNow(convertInt(init.size()), 4); + conn.SendNow(init); + conn.SendNow("\000\005codec\002", 8); + conn.SendNow(convertInt(codec.size()), 4); + conn.SendNow(codec); + conn.SendNow("\000\004type\002", 7); + conn.SendNow(convertInt(type.size()), 4); + conn.SendNow(type); + if (type == "audio"){ + conn.SendNow("\000\004rate\001", 7); + conn.SendNow(convertLongLong(rate), 8); + conn.SendNow("\000\004size\001", 7); + conn.SendNow(convertLongLong(size), 8); + conn.SendNow("\000\010channels\001", 11); + conn.SendNow(convertLongLong(channels), 8); + }else if (type == "video"){ + conn.SendNow("\000\005width\001", 8); + conn.SendNow(convertLongLong(width), 8); + conn.SendNow("\000\006height\001", 9); + conn.SendNow(convertLongLong(height), 8); + conn.SendNow("\000\004fpks\001", 7); + conn.SendNow(convertLongLong(fpks), 8); + } + conn.SendNow("\000\000\356", 3);//End this track Object + } + + void Track::send(Socket::Connection & conn){ + conn.SendNow(convertShort(getIdentifier().size()), 2); + conn.SendNow(getIdentifier()); + conn.SendNow("\340", 1);//Begin track object + conn.SendNow("\000\011fragments\002", 12); + conn.SendNow(convertInt(fragments.size()*11), 4); + for (std::deque::iterator it = fragments.begin(); it != fragments.end(); it++){ + conn.SendNow(it->getData(), 11); + } + conn.SendNow("\000\004keys\002", 7); + conn.SendNow(convertInt(keys.size()*16), 4); + for (std::deque::iterator it = keys.begin(); it != keys.end(); it++){ + conn.SendNow(it->getData(), 16); + } + conn.SendNow("\000\005parts\002", 8); + conn.SendNow(convertInt(parts.size()*8), 4); + for (std::deque::iterator it = parts.begin(); it != parts.end(); it++){ + conn.SendNow(it->getData(), 8); + } + conn.SendNow("\000\007trackid\001", 10); + conn.SendNow(convertLongLong(trackID), 8); + conn.SendNow("\000\006length\001", 9); + conn.SendNow(convertLongLong(length), 8); + conn.SendNow("\000\007firstms\001", 10); + conn.SendNow(convertLongLong(firstms), 8); + conn.SendNow("\000\006lastms\001", 9); + conn.SendNow(convertLongLong(lastms), 8); + conn.SendNow("\000\003bps\001", 6); + conn.SendNow(convertLongLong(bps), 8); + conn.SendNow("\000\004init\002", 7); + conn.SendNow(convertInt(init.size()), 4); + conn.SendNow(init); + conn.SendNow("\000\005codec\002", 8); + conn.SendNow(convertInt(codec.size()), 4); + conn.SendNow(codec); + conn.SendNow("\000\004type\002", 7); + conn.SendNow(convertInt(type.size()), 4); + conn.SendNow(type); + if (type == "audio"){ + conn.SendNow("\000\004rate\001", 7); + conn.SendNow(convertLongLong(rate), 8); + conn.SendNow("\000\004size\001", 7); + conn.SendNow(convertLongLong(size), 8); + conn.SendNow("\000\010channels\001", 11); + conn.SendNow(convertLongLong(channels), 8); + }else if (type == "video"){ + conn.SendNow("\000\005width\001", 8); + conn.SendNow(convertLongLong(width), 8); + conn.SendNow("\000\006height\001", 9); + conn.SendNow(convertLongLong(height), 8); + conn.SendNow("\000\004fpks\001", 7); + conn.SendNow(convertLongLong(fpks), 8); + } + conn.SendNow("\000\000\356", 3);//End this track Object + } + + void readOnlyMeta::send(Socket::Connection & conn){ + int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + 21; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + dataLen += it->second.getSendLen(); + } + conn.SendNow(DTSC::Magic_Header, 4); + conn.SendNow(convertInt(dataLen), 4); + conn.SendNow("\340\000\006tracks\340", 10); + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + it->second.send(conn); + } + conn.SendNow("\000\000\356", 3); + if (vod){ + conn.SendNow("\000\003vod\001", 6); + conn.SendNow(convertLongLong(1), 8); + } + if (live){ + conn.SendNow("\000\004live\001", 7); + conn.SendNow(convertLongLong(1), 8); + } + if (merged){ + conn.SendNow("\000\006merged\001", 9); + conn.SendNow(convertLongLong(1), 8); + } + conn.SendNow("\000\012moreheader\001", 13); + conn.SendNow(convertLongLong(moreheader), 8); + conn.SendNow("\000\000\356", 3);//End global object + } + + void Meta::send(Socket::Connection & conn){ + int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + 21; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + dataLen += it->second.getSendLen(); + } + conn.SendNow(DTSC::Magic_Header, 4); + conn.SendNow(convertInt(dataLen), 4); + conn.SendNow("\340\000\006tracks\340", 10); + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + it->second.send(conn); + } + conn.SendNow("\000\000\356", 3);//End tracks object + if (vod){ + conn.SendNow("\000\003vod\001", 6); + conn.SendNow(convertLongLong(1), 8); + } + if (live){ + conn.SendNow("\000\004live\001", 7); + conn.SendNow(convertLongLong(1), 8); + } + if (merged){ + conn.SendNow("\000\006merged\001", 9); + conn.SendNow(convertLongLong(1), 8); + } + conn.SendNow("\000\012moreheader\001", 13); + conn.SendNow(convertLongLong(moreheader), 8); + conn.SendNow("\000\000\356", 3);//End global object + } + + JSON::Value readOnlyTrack::toJSON(){ + JSON::Value result; + if (fragments){ + result["fragments"] = std::string((char*)fragments, fragLen * 11); + } + if (keys){ + result["keys"] = std::string((char*)keys, keyLen * 16); + } + if (parts){ + result["parts"] = std::string((char*)parts, partLen * 8); + } + result["trackid"] = trackID; + result["length"] = length; + result["firstms"] = firstms; + result["lastms"] = lastms; + result["bps"] = bps; + if (missedFrags){ + result["missed_frags"] = missedFrags; + } + result["codec"] = codec; + result["type"] = type; + result["init"] = init; + if (type == "audio"){ + result["rate"] = rate; + result["size"] = size; + result["channels"] = channels; + }else if (type == "video"){ + result["width"] = width; + result["height"] = height; + result["fpks"] = fpks; + } + return result; + } + + JSON::Value Track::toJSON(){ + JSON::Value result; + std::string tmp; + tmp.reserve(fragments.size() * 11); + for (std::deque::iterator it = fragments.begin(); it != fragments.end(); it++){ + tmp.append(it->getData(), 11); + } + result["fragments"] = tmp; + tmp = ""; + tmp.reserve(keys.size() * 16); + for (std::deque::iterator it = keys.begin(); it != keys.end(); it++){ + tmp.append(it->getData(), 16); + } + result["keys"] = tmp; + tmp = ""; + tmp.reserve(parts.size() * 8); + for (std::deque::iterator it = parts.begin(); it != parts.end(); it++){ + tmp.append(it->getData(), 8); + } + result["parts"] = tmp; + result["trackid"] = trackID; + result["length"] = length; + result["firstms"] = firstms; + result["lastms"] = lastms; + result["bps"] = bps; + if (missedFrags){ + result["missed_frags"] = missedFrags; + } + result["codec"] = codec; + result["type"] = type; + result["init"] = init; + if (type == "audio"){ + result["rate"] = rate; + result["size"] = size; + result["channels"] = channels; + }else if (type == "video"){ + result["width"] = width; + result["height"] = height; + result["fpks"] = fpks; + } + return result; + } + + JSON::Value Meta::toJSON(){ + JSON::Value result; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if (!it->second.trackID){continue;} + result["tracks"][it->second.getIdentifier()] = it->second.toJSON(); + } + if (vod){ + result["vod"] = 1ll; + } + if (live){ + result["live"] = 1ll; + } + if (merged){ + result["merged"] = 1ll; + } + if (bufferWindow){ + result["buffer_window"]; + } + result["moreheader"] = moreheader; + return result; + } + + JSON::Value readOnlyMeta::toJSON(){ + JSON::Value result; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if (!it->second.trackID){continue;} + result["tracks"][it->second.getIdentifier()] = it->second.toJSON(); + } + if (vod){ + result["vod"] = 1ll; + } + if (live){ + result["live"] = 1ll; + } + if (merged){ + result["merged"] = 1ll; + } + result["moreheader"] = moreheader; + if (bufferWindow){ + result["buffer_window"]; + } + return result; + } + + void Meta::reset(){ + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + it->second.reset(); + } + } + + bool readOnlyMeta::isFixed(){ + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if ( !it->second.keyLen || !(it->second.keys[it->second.keyLen - 1].getBpos())){ + return false; + } + } + return true; + } + + bool Meta::isFixed(){ + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if (!it->second.keys.size() || !(it->second.keys.rbegin()->getBpos())){ + return false; + } + } + return true; + } +} diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 231f0ab7..f32271b0 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -364,22 +364,18 @@ FLV::Tag & FLV::Tag::operator=(const FLV::Tag& O){ /// Takes the DTSC data and makes it into FLV. bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ std::string meta_str; - JSON::Value & track = S.getTrackById(S.getPacket()["trackid"].asInt()); + DTSC::Track & track = S.metadata.tracks[S.getPacket()["trackid"].asInt()]; switch (S.lastType()){ case DTSC::VIDEO: len = S.lastData().length() + 16; - if (track && track.isMember("codec")){ - if (track["codec"].asStringRef() == "H264"){ - len += 4; - } + if (track.codec == "H264"){ + len += 4; } break; case DTSC::AUDIO: len = S.lastData().length() + 16; - if (track && track.isMember("codec")){ - if (track["codec"].asStringRef() == "AAC"){ - len += 1; - } + if (track.codec == "AAC"){ + len += 1; } break; case DTSC::META:{ @@ -418,10 +414,10 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ offset(S.getPacket()["offset"].asInt()); } data[11] = 0; - if (track.isMember("codec") && track["codec"].asStringRef() == "H264"){ + if (track.codec == "H264"){ data[11] += 7; } - if (track.isMember("codec") && track["codec"].asStringRef() == "H263"){ + if (track.codec == "H263"){ data[11] += 2; } if (S.getPacket().isMember("keyframe")){ @@ -442,13 +438,13 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[12] = 1; //raw AAC data, not sequence header } data[11] = 0; - if (track.isMember("codec") && track["codec"].asString() == "AAC"){ + if (track.codec == "AAC"){ data[11] += 0xA0; } - if (track.isMember("codec") && track["codec"].asString() == "MP3"){ + if (track.codec == "MP3"){ data[11] += 0x20; } - unsigned int datarate = track["rate"].asInt(); + unsigned int datarate = track.rate; if (datarate >= 44100){ data[11] += 0x0C; }else if (datarate >= 22050){ @@ -456,10 +452,10 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ }else if (datarate >= 11025){ data[11] += 0x04; } - if (track["size"].asInt() == 16){ + if (track.size == 16){ data[11] += 0x02; } - if (track["channels"].asInt() > 1){ + if (track.channels > 1){ data[11] += 0x01; } break; @@ -508,26 +504,20 @@ void FLV::Tag::setLen(){ data[ --i] = (len4) & 0xFF; } -/// FLV Video init data loader function from DTSC. -/// Takes the DTSC Video init data and makes it into FLV. -/// Assumes init data is available - so check before calling! -bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ - return DTSCVideoInit(S.metadata["video"]); -} - -bool FLV::Tag::DTSCVideoInit(JSON::Value & video){ +/// FLV Video init data loader function from JSON. +bool FLV::Tag::DTSCVideoInit(DTSC::Track & video){ //Unknown? Assume H264. - if (video["codec"].asString() == "?"){ - video["codec"] = "H264"; + if (video.codec == "?"){ + video.codec = "H264"; } - if (video["codec"].asString() == "H264"){ - len = video["init"].asString().length() + 20; + if (video.codec == "H264"){ + len = video.init.size() + 20; } if (len > 0){ if ( !checkBufferSize()){ return false; } - memcpy(data + 16, video["init"].asString().c_str(), len - 20); + memcpy(data + 16, video.init.c_str(), len - 20); data[12] = 0; //H264 sequence header data[13] = 0; data[14] = 0; @@ -546,36 +536,30 @@ bool FLV::Tag::DTSCVideoInit(JSON::Value & video){ return true; } -/// FLV Audio init data loader function from DTSC. -/// Takes the DTSC Audio init data and makes it into FLV. -/// Assumes init data is available - so check before calling! -bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ - return DTSCAudioInit(S.metadata["audio"]); -} - -bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ +/// FLV Audio init data loader function from JSON. +bool FLV::Tag::DTSCAudioInit(DTSC::Track & audio){ len = 0; //Unknown? Assume AAC. - if (audio["codec"].asString() == "?"){ - audio["codec"] = "AAC"; + if (audio.codec == "?"){ + audio.codec = "AAC"; } - if (audio["codec"].asString() == "AAC"){ - len = audio["init"].asString().length() + 17; + if (audio.codec == "AAC"){ + len = audio.init.size() + 17; } if (len > 0){ if ( !checkBufferSize()){ return false; } - memcpy(data + 13, audio["init"].asString().c_str(), len - 17); + memcpy(data + 13, audio.init.c_str(), len - 17); data[12] = 0; //AAC sequence header data[11] = 0; - if (audio["codec"].asString() == "AAC"){ + if (audio.codec == "AAC"){ data[11] += 0xA0; } - if (audio["codec"].asString() == "MP3"){ + if (audio.codec == "MP3"){ data[11] += 0x20; } - unsigned int datarate = audio["rate"].asInt(); + unsigned int datarate = audio.rate; if (datarate >= 44100){ data[11] += 0x0C; }else if (datarate >= 22050){ @@ -583,10 +567,10 @@ bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ }else if (datarate >= 11025){ data[11] += 0x04; } - if (audio["size"].asInt() == 16){ + if (audio.size == 16){ data[11] += 0x02; } - if (audio["channels"].asInt() > 1){ + if (audio.channels > 1){ data[11] += 0x01; } @@ -606,34 +590,34 @@ bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ /// FLV metadata loader function from DTSC. /// Takes the DTSC metadata and makes it into FLV. /// Assumes metadata is available - so check before calling! -bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Value & audioRef){ +bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef){ //Unknown? Assume AAC. - if (audioRef["codec"].asString() == "?"){ - audioRef["codec"] = "AAC"; + if (audioRef.codec == "?"){ + audioRef.codec = "AAC"; } //Unknown? Assume H264. - if (videoRef["codec"].asString() == "?"){ - videoRef["codec"] = "H264"; + if (videoRef.codec == "?"){ + videoRef.codec = "H264"; } AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.isMember("length")){ - amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["length"].asInt(), AMF::AMF0_NUMBER)); + if (S.metadata.vod){ + amfdata.getContentP(1)->addContent(AMF::Object("duration", videoRef.lastms / 1000, AMF::AMF0_NUMBER)); amfdata.getContentP(1)->addContent(AMF::Object("moovPosition", 40, AMF::AMF0_NUMBER)); AMF::Object keys("keyframes", AMF::AMF0_OBJECT); keys.addContent(AMF::Object("filepositions", AMF::AMF0_STRICT_ARRAY)); keys.addContent(AMF::Object("times", AMF::AMF0_STRICT_ARRAY)); int total_byterate = 0; if (videoRef){ - total_byterate += videoRef["bps"].asInt(); + total_byterate += videoRef.bps; } if (audioRef){ - total_byterate += audioRef["bps"].asInt(); + total_byterate += audioRef.bps; } - for (int i = 0; i < S.metadata["length"].asInt(); ++i){ //for each second in the file + for (int i = 0; i < videoRef.lastms / 1000; ++i){ //for each second in the file keys.getContentP(0)->addContent(AMF::Object("", i * total_byterate, AMF::AMF0_NUMBER)); //multiply by byterate for fake byte positions keys.getContentP(1)->addContent(AMF::Object("", i, AMF::AMF0_NUMBER)); //seconds } @@ -641,62 +625,45 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Valu } if (videoRef){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (videoRef["codec"].asString() == "H264"){ + if (videoRef.codec == "H264"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", (std::string)"avc1")); } - if (videoRef["codec"].asString() == "VP6"){ + if (videoRef.codec == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); } - if (videoRef["codec"].asString() == "H263"){ + if (videoRef.codec == "H263"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); } - if (videoRef.isMember("width")){ - amfdata.getContentP(1)->addContent(AMF::Object("width", videoRef["width"].asInt(), AMF::AMF0_NUMBER)); - } - if (videoRef.isMember("height")){ - amfdata.getContentP(1)->addContent(AMF::Object("height", videoRef["height"].asInt(), AMF::AMF0_NUMBER)); - } - if (videoRef.isMember("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)videoRef["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); - } - if (videoRef.isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)videoRef["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); - } + amfdata.getContentP(1)->addContent(AMF::Object("width", videoRef.width, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("height", videoRef.height, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)videoRef.fpks / 1000.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)videoRef.bps * 128.0, AMF::AMF0_NUMBER)); } if (audioRef){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (audioRef["codec"].asString() == "AAC"){ + if (audioRef.codec == "AAC"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp4a")); } - if (audioRef["codec"].asString() == "MP3"){ + if (audioRef.codec == "MP3"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp3")); } - if (audioRef.isMember("channels")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", audioRef["channels"].asInt(), AMF::AMF0_NUMBER)); - } - if (audioRef.isMember("rate")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", audioRef["rate"].asInt(), AMF::AMF0_NUMBER)); - } - if (audioRef.isMember("size")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", audioRef["size"].asInt(), AMF::AMF0_NUMBER)); - } - if (audioRef.isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)audioRef["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); - } + amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", audioRef.channels, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", audioRef.rate, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", audioRef.size, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)audioRef.bps * 128.0, AMF::AMF0_NUMBER)); } AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); int i = 0; if (audioRef){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent( - AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)audioRef["rate"].asInt()), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", audioRef["rate"].asInt(), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata.length) * ((double)audioRef.rate), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", audioRef.rate, AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (audioRef["codec"].asString() == "AAC"){ + if (audioRef.codec == "AAC"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp4a")); } - if (audioRef["codec"].asString() == "MP3"){ + if (audioRef.codec == "MP3"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp3")); } ++i; @@ -704,16 +671,16 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Valu if (videoRef){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.getContentP(i)->addContent( - AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)videoRef["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)videoRef["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + AMF::Object("length", ((double)videoRef.lastms / 1000) * ((double)videoRef.fpks / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)videoRef.fpks / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (videoRef["codec"].asString() == "H264"){ + if (videoRef.codec == "H264"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"avc1")); } - if (videoRef["codec"].asString() == "VP6"){ + if (videoRef.codec == "VP6"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6")); } - if (videoRef["codec"].asString() == "H263"){ + if (videoRef.codec == "H263"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"h263")); } ++i; @@ -1061,12 +1028,6 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ if ( !metadata["tracks"]["track1"].isMember("bps")){ metadata["tracks"]["track1"]["bps"] = 0; } - if ( !metadata["tracks"]["track1"].isMember("keyms")){ - metadata["tracks"]["track1"]["keyms"] = 0; - } - if ( !metadata["tracks"]["track1"].isMember("keyvar")){ - metadata["tracks"]["track1"]["keyvar"] = 0; - } } return pack_out; //empty } diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 1c1e0446..591b8337 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -47,11 +47,9 @@ namespace FLV { //loader functions bool ChunkLoader(const RTMPStream::Chunk& O); bool DTSCLoader(DTSC::Stream & S); - bool DTSCVideoInit(DTSC::Stream & S); - bool DTSCVideoInit(JSON::Value & video); - bool DTSCAudioInit(DTSC::Stream & S); - bool DTSCAudioInit(JSON::Value & audio); - bool DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Value & audioRef); + bool DTSCVideoInit(DTSC::Track & video); + bool DTSCAudioInit(DTSC::Track & audio); + bool DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef); JSON::Value toJSON(JSON::Value & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool FileLoader(FILE * f); diff --git a/lib/ogg.h b/lib/ogg.h index 368adf5b..6d31edb1 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include -#include"dtsc.h" +#include +#include +#include +#include "dtsc.h" #include "theora.h" #include "vorbis.h" #include "json.h"