Metadata upgrades as well as improvements to the buffer in terms of debugging and code maintainability.
This commit is contained in:
parent
bb1971e961
commit
cde42ce36e
25 changed files with 390 additions and 664 deletions
|
@ -17,108 +17,13 @@ namespace Analysers {
|
||||||
///\return The return code of the analyser.
|
///\return The return code of the analyser.
|
||||||
int analyseDTSC(Util::Config conf){
|
int analyseDTSC(Util::Config conf){
|
||||||
DTSC::File F(conf.getString("filename"));
|
DTSC::File F(conf.getString("filename"));
|
||||||
JSON::Value meta = F.getMeta();
|
std::cout << F.getMeta().toJSON().toPrettyString() << std::endl;
|
||||||
std::cout << meta.toPrettyString() << std::endl;
|
|
||||||
JSON::Value pack;
|
|
||||||
|
|
||||||
long long unsigned int firstpack = 0;
|
|
||||||
long long unsigned int nowpack = 0;
|
|
||||||
long long unsigned int lastaudio = 0;
|
|
||||||
long long unsigned int lastvideo = 0;
|
|
||||||
long long unsigned int lastkey = 0;
|
|
||||||
long long unsigned int totalvideo = 0;
|
|
||||||
long long unsigned int totalaudio = 0;
|
|
||||||
long long unsigned int keyframes = 0;
|
|
||||||
long long unsigned int key_min = 0xffffffff;
|
|
||||||
long long unsigned int key_max = 0;
|
|
||||||
long long unsigned int vid_min = 0xffffffff;
|
|
||||||
long long unsigned int vid_max = 0;
|
|
||||||
long long unsigned int aud_min = 0xffffffff;
|
|
||||||
long long unsigned int aud_max = 0;
|
|
||||||
long long unsigned int bfrm_min = 0xffffffff;
|
|
||||||
long long unsigned int bfrm_max = 0;
|
|
||||||
long long unsigned int bps = 0;
|
|
||||||
|
|
||||||
F.getMeta().null();
|
|
||||||
|
|
||||||
F.parseNext();
|
F.parseNext();
|
||||||
while (F.getJSON()){
|
while (F.getJSON()){
|
||||||
nowpack = F.getJSON()["time"].asInt();
|
|
||||||
std::cout << F.getJSON().toPrettyString() << std::endl;
|
std::cout << F.getJSON().toPrettyString() << std::endl;
|
||||||
if (firstpack == 0){
|
|
||||||
firstpack = nowpack;
|
|
||||||
}
|
|
||||||
if (F.getJSON()["datatype"].asString() == "audio"){
|
|
||||||
if (lastaudio != 0 && (nowpack - lastaudio) != 0){
|
|
||||||
bps = F.getJSON()["data"].asString().size() / (nowpack - lastaudio);
|
|
||||||
if (bps < aud_min){
|
|
||||||
aud_min = bps;
|
|
||||||
}
|
|
||||||
if (bps > aud_max){
|
|
||||||
aud_max = bps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalaudio += F.getJSON()["data"].asString().size();
|
|
||||||
lastaudio = nowpack;
|
|
||||||
}
|
|
||||||
if (F.getJSON()["datatype"].asString() == "video"){
|
|
||||||
if (lastvideo != 0 && (nowpack - lastvideo) != 0){
|
|
||||||
bps = F.getJSON()["data"].asString().size() / (nowpack - lastvideo);
|
|
||||||
if (bps < vid_min){
|
|
||||||
vid_min = bps;
|
|
||||||
}
|
|
||||||
if (bps > vid_max){
|
|
||||||
vid_max = bps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (F.getJSON()["keyframe"].asInt() != 0){
|
|
||||||
if (lastkey != 0){
|
|
||||||
bps = nowpack - lastkey;
|
|
||||||
if (bps < key_min){
|
|
||||||
key_min = bps;
|
|
||||||
}
|
|
||||||
if (bps > key_max){
|
|
||||||
key_max = bps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyframes++;
|
|
||||||
lastkey = nowpack;
|
|
||||||
}
|
|
||||||
if (F.getJSON()["offset"].asInt() != 0){
|
|
||||||
bps = F.getJSON()["offset"].asInt();
|
|
||||||
if (bps < bfrm_min){
|
|
||||||
bfrm_min = bps;
|
|
||||||
}
|
|
||||||
if (bps > bfrm_max){
|
|
||||||
bfrm_max = bps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalvideo += F.getJSON()["data"].asString().size();
|
|
||||||
lastvideo = nowpack;
|
|
||||||
}
|
|
||||||
F.parseNext();
|
F.parseNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << std::endl << "Summary:" << std::endl;
|
|
||||||
meta["length"] = (long long int)((nowpack - firstpack) / 1000);
|
|
||||||
if (meta.isMember("audio")){
|
|
||||||
meta["audio"]["bps"] = (long long int)(totalaudio / ((lastaudio - firstpack) / 1000));
|
|
||||||
std::cout << " Audio: " << meta["audio"]["codec"].asString() << std::endl;
|
|
||||||
std::cout << " Bitrate: " << meta["audio"]["bps"].asInt() << std::endl;
|
|
||||||
}
|
|
||||||
if (meta.isMember("video")){
|
|
||||||
meta["video"]["bps"] = (long long int)(totalvideo / ((lastvideo - firstpack) / 1000));
|
|
||||||
meta["video"]["keyms"] = (long long int)((lastvideo - firstpack) / keyframes);
|
|
||||||
if (meta["video"]["keyms"].asInt() - key_min > key_max - meta["video"]["keyms"].asInt()){
|
|
||||||
meta["video"]["keyvar"] = (long long int)(meta["video"]["keyms"].asInt() - key_min);
|
|
||||||
}else{
|
|
||||||
meta["video"]["keyvar"] = (long long int)(key_max - meta["video"]["keyms"].asInt());
|
|
||||||
}
|
|
||||||
std::cout << " Video: " << meta["video"]["codec"].asString() << std::endl;
|
|
||||||
std::cout << " Bitrate: " << meta["video"]["bps"].asInt() << std::endl;
|
|
||||||
std::cout << " Keyframes: " << meta["video"]["keyms"].asInt() << "~" << meta["video"]["keyvar"].asInt() << std::endl;
|
|
||||||
std::cout << " B-frames: " << bfrm_min << " - " << bfrm_max << std::endl;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,9 @@ namespace Buffer {
|
||||||
///\brief A function running in a thread to send all statistics.
|
///\brief A function running in a thread to send all statistics.
|
||||||
///\param empty A null pointer.
|
///\param empty A null pointer.
|
||||||
void handleStats(void * empty){
|
void handleStats(void * empty){
|
||||||
|
#ifdef _TTHREAD_POSIX_
|
||||||
|
pthread_setname_np(pthread_self(), "StatsHandler");
|
||||||
|
#endif
|
||||||
if (empty != 0){
|
if (empty != 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +55,9 @@ namespace Buffer {
|
||||||
///\brief A function to handle input data.
|
///\brief A function to handle input data.
|
||||||
///\param conn A socket reference.
|
///\param conn A socket reference.
|
||||||
void handlePushIn(Socket::Connection & conn){
|
void handlePushIn(Socket::Connection & conn){
|
||||||
|
#ifdef _TTHREAD_POSIX_
|
||||||
|
pthread_setname_np(pthread_self(), "Push Input");
|
||||||
|
#endif
|
||||||
conn.setBlocking(true);
|
conn.setBlocking(true);
|
||||||
while (buffer_running && conn.connected()){
|
while (buffer_running && conn.connected()){
|
||||||
if (conn.spool()){
|
if (conn.spool()){
|
||||||
|
@ -70,6 +76,9 @@ namespace Buffer {
|
||||||
if (empty != 0){
|
if (empty != 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef _TTHREAD_POSIX_
|
||||||
|
pthread_setname_np(pthread_self(), "Standard Input");
|
||||||
|
#endif
|
||||||
long long int timeDiff = 0; //difference between local time and stream time
|
long long int timeDiff = 0; //difference between local time and stream time
|
||||||
unsigned int lastPacket = 0; //last parsed packet timestamp
|
unsigned int lastPacket = 0; //last parsed packet timestamp
|
||||||
std::string inBuffer;
|
std::string inBuffer;
|
||||||
|
@ -105,7 +114,10 @@ namespace Buffer {
|
||||||
user * usr = (user*)v_usr;
|
user * usr = (user*)v_usr;
|
||||||
thisStream->addUser(usr);
|
thisStream->addUser(usr);
|
||||||
#if DEBUG >= 5
|
#if DEBUG >= 5
|
||||||
std::cerr << "Thread launched for user " << usr->MyStr << ", socket number " << usr->S.getSocket() << std::endl;
|
std::cerr << "Thread launched for user " << usr->sID << ", socket number " << usr->S.getSocket() << std::endl;
|
||||||
|
#endif
|
||||||
|
#ifdef _TTHREAD_POSIX_
|
||||||
|
pthread_setname_np(pthread_self(), usr->sID.c_str());
|
||||||
#endif
|
#endif
|
||||||
usr->myRing = thisStream->getRing();
|
usr->myRing = thisStream->getRing();
|
||||||
thisStream->sendMeta(usr->S);
|
thisStream->sendMeta(usr->S);
|
||||||
|
@ -122,10 +134,10 @@ namespace Buffer {
|
||||||
if (usr->myRing->playCount < 1 || usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt()){
|
if (usr->myRing->playCount < 1 || usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt()){
|
||||||
usr->myRing->playCount = 0;
|
usr->myRing->playCount = 0;
|
||||||
JSON::Value pausemark;
|
JSON::Value pausemark;
|
||||||
pausemark["datatype"] = "pause_marker";
|
pausemark["trackid"] = 0ll;
|
||||||
|
pausemark["mark"] = "pause";
|
||||||
pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt();
|
pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt();
|
||||||
pausemark.toPacked();
|
pausemark.sendTo(usr->S);
|
||||||
usr->S.SendNow(pausemark.toNetPacked());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,10 +156,10 @@ namespace Buffer {
|
||||||
if (usr->myRing->playCount < 1 || usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt()){
|
if (usr->myRing->playCount < 1 || usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt()){
|
||||||
usr->myRing->playCount = 0;
|
usr->myRing->playCount = 0;
|
||||||
JSON::Value pausemark;
|
JSON::Value pausemark;
|
||||||
pausemark["datatype"] = "pause_marker";
|
pausemark["trackid"] = 0ll;
|
||||||
|
pausemark["mark"] = "pause";
|
||||||
pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt();
|
pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt();
|
||||||
pausemark.toPacked();
|
pausemark.sendTo(usr->S);
|
||||||
usr->S.SendNow(pausemark.toNetPacked());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,6 +287,9 @@ namespace Buffer {
|
||||||
}
|
}
|
||||||
SS.setBlocking(false);
|
SS.setBlocking(false);
|
||||||
conf.activate();
|
conf.activate();
|
||||||
|
#ifdef _TTHREAD_POSIX_
|
||||||
|
pthread_setname_np(pthread_self(), "Main accepter");
|
||||||
|
#endif
|
||||||
thisStream = Stream::get();
|
thisStream = Stream::get();
|
||||||
thisStream->setName(name);
|
thisStream->setName(name);
|
||||||
thisStream->setBufferTime(conf.getInteger("time"));
|
thisStream->setBufferTime(conf.getInteger("time"));
|
||||||
|
@ -299,7 +314,7 @@ namespace Buffer {
|
||||||
//starts a thread for every accepted connection
|
//starts a thread for every accepted connection
|
||||||
incoming = SS.accept(true);
|
incoming = SS.accept(true);
|
||||||
if (incoming.connected()){
|
if (incoming.connected()){
|
||||||
tthread::thread thisUser(handleUser, (void *)new user(incoming, userId++));
|
tthread::thread thisUser(handleUser, (void *)new user(incoming, ++userId));
|
||||||
thisUser.detach();
|
thisUser.detach();
|
||||||
}else{
|
}else{
|
||||||
Util::sleep(50);//sleep 50ms
|
Util::sleep(50);//sleep 50ms
|
||||||
|
|
|
@ -17,8 +17,7 @@ namespace Buffer {
|
||||||
creator.lock();
|
creator.lock();
|
||||||
if (ref == 0){
|
if (ref == 0){
|
||||||
ref = new Stream();
|
ref = new Stream();
|
||||||
ref->metadata["tracks"] = 1ll;
|
ref->metadata.live = true;
|
||||||
ref->metadata["live"] = 1ll;
|
|
||||||
}
|
}
|
||||||
creator.unlock();
|
creator.unlock();
|
||||||
}
|
}
|
||||||
|
@ -194,7 +193,7 @@ namespace Buffer {
|
||||||
void Stream::sendMeta(Socket::Connection & s){
|
void Stream::sendMeta(Socket::Connection & s){
|
||||||
if (metadata){
|
if (metadata){
|
||||||
rw_mutex.lock();
|
rw_mutex.lock();
|
||||||
metadata.sendTo(s);
|
metadata.send(s);
|
||||||
rw_mutex.unlock();
|
rw_mutex.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Buffer {
|
||||||
///Also prints "User connected" text to stdout.
|
///Also prints "User connected" text to stdout.
|
||||||
///\param fd A connection to the user.
|
///\param fd A connection to the user.
|
||||||
user::user(Socket::Connection fd, long long ID){
|
user::user(Socket::Connection fd, long long ID){
|
||||||
sID = JSON::Value(ID).asStringRef();
|
sID = JSON::Value(ID).asString();
|
||||||
S = fd;
|
S = fd;
|
||||||
curr_up = 0;
|
curr_up = 0;
|
||||||
curr_down = 0;
|
curr_down = 0;
|
||||||
|
|
|
@ -81,15 +81,17 @@ int main(int argc, char** argv){
|
||||||
Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin));
|
Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin));
|
||||||
|
|
||||||
DTSC::File source = DTSC::File(conf.getString("filename"));
|
DTSC::File source = DTSC::File(conf.getString("filename"));
|
||||||
in_out.SendNow(source.getMeta().toNetPacked());
|
|
||||||
|
|
||||||
if ( !DTSC::isFixed(source.getMeta())){
|
if ( !source.getMeta().isFixed()){
|
||||||
std::cerr << "Encountered a non-fixed file." << std::endl;
|
std::cerr << "Encountered a non-fixed file." << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
source.getMeta().send(in_out);
|
||||||
|
|
||||||
JSON::Value pausemark;
|
JSON::Value pausemark;
|
||||||
pausemark["datatype"] = "pause_marker";
|
pausemark["trackid"] = 0ll;
|
||||||
|
pausemark["mark"] = "pause";
|
||||||
pausemark["time"] = (long long int)0;
|
pausemark["time"] = (long long int)0;
|
||||||
|
|
||||||
Socket::Connection StatsSocket = Socket::Connection(Util::getTmpFolder() + "statistics", true);
|
Socket::Connection StatsSocket = Socket::Connection(Util::getTmpFolder() + "statistics", true);
|
||||||
|
@ -140,11 +142,12 @@ int main(int argc, char** argv){
|
||||||
json_sts["vod"]["now"] = Util::epoch();
|
json_sts["vod"]["now"] = Util::epoch();
|
||||||
json_sts["vod"]["start"] = Util::epoch() - sts.conntime;
|
json_sts["vod"]["start"] = Util::epoch() - sts.conntime;
|
||||||
if ( !meta_sent){
|
if ( !meta_sent){
|
||||||
json_sts["vod"]["meta"] = source.getMeta();
|
json_sts["vod"]["meta"] = source.getMeta().toJSON();
|
||||||
json_sts["vod"]["meta"]["is_fixed"] = 1;
|
json_sts["vod"]["meta"]["is_fixed"] = 1;
|
||||||
for (JSON::ObjIter oIt = json_sts["vod"]["meta"]["tracks"].ObjBegin(); oIt != json_sts["vod"]["meta"]["tracks"].ObjEnd(); oIt++){
|
for (JSON::ObjIter oIt = json_sts["vod"]["meta"]["tracks"].ObjBegin(); oIt != json_sts["vod"]["meta"]["tracks"].ObjEnd(); oIt++){
|
||||||
oIt->second.removeMember("keys");
|
oIt->second.removeMember("keys");
|
||||||
oIt->second.removeMember("frags");
|
oIt->second.removeMember("fragments");
|
||||||
|
oIt->second.removeMember("parts");
|
||||||
}
|
}
|
||||||
meta_sent = true;
|
meta_sent = true;
|
||||||
}
|
}
|
||||||
|
@ -237,8 +240,7 @@ int main(int argc, char** argv){
|
||||||
std::cerr << "Completed VoD request in MistPlayer (" << (Util::getMS() - bench) << "ms)" << std::endl;
|
std::cerr << "Completed VoD request in MistPlayer (" << (Util::getMS() - bench) << "ms)" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
pausemark["time"] = source.getJSON()["time"];
|
pausemark["time"] = source.getJSON()["time"];
|
||||||
pausemark.netPrepare();
|
pausemark.sendTo(in_out);
|
||||||
in_out.SendNow(pausemark.toNetPacked());
|
|
||||||
in_out.setBlocking(true);
|
in_out.setBlocking(true);
|
||||||
}else{
|
}else{
|
||||||
lasttime = Util::epoch();
|
lasttime = Util::epoch();
|
||||||
|
|
|
@ -27,16 +27,16 @@
|
||||||
/// Holds everything unique to HTTP Connectors.
|
/// Holds everything unique to HTTP Connectors.
|
||||||
namespace Connector_HTTP {
|
namespace Connector_HTTP {
|
||||||
|
|
||||||
std::set<std::string> videoTracks;///<< Holds valid video tracks for playback
|
std::set<int> videoTracks;///<< Holds valid video tracks for playback
|
||||||
long long int audioTrack = 0;///<< Holds audio track ID for playback
|
long long int audioTrack = 0;///<< Holds audio track ID for playback
|
||||||
void getTracks(JSON::Value & metadata){
|
void getTracks(DTSC::Meta & metadata){
|
||||||
videoTracks.clear();
|
videoTracks.clear();
|
||||||
for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){
|
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||||
if (it->second["type"] == "video"){
|
if (it->second.type == "video"){
|
||||||
videoTracks.insert(it->first);
|
videoTracks.insert(it->first);
|
||||||
}
|
}
|
||||||
if (it->second["type"] == "audio"){
|
if (it->second.type == "audio"){
|
||||||
audioTrack = it->second["trackid"].asInt();
|
audioTrack = it->first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ namespace Connector_HTTP {
|
||||||
///\param metadata The current metadata, used to generate the index.
|
///\param metadata The current metadata, used to generate the index.
|
||||||
///\param fragnum The index of the current fragment
|
///\param fragnum The index of the current fragment
|
||||||
///\return The generated bootstrap.
|
///\return The generated bootstrap.
|
||||||
std::string dynamicBootstrap(std::string & streamName, JSON::Value & metadata, bool isLive = false, int fragnum = 0){
|
std::string dynamicBootstrap(std::string & streamName, DTSC::Track & trackMeta, bool isLive = false, int fragnum = 0){
|
||||||
std::string empty;
|
std::string empty;
|
||||||
|
|
||||||
MP4::ASRT asrt;
|
MP4::ASRT asrt;
|
||||||
|
@ -57,7 +57,7 @@ namespace Connector_HTTP {
|
||||||
if (isLive){
|
if (isLive){
|
||||||
asrt.setSegmentRun(1, 4294967295ul, 0);
|
asrt.setSegmentRun(1, 4294967295ul, 0);
|
||||||
}else{
|
}else{
|
||||||
asrt.setSegmentRun(1, metadata["keys"].size(), 0);
|
asrt.setSegmentRun(1, trackMeta.keys.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
MP4::AFRT afrt;
|
MP4::AFRT afrt;
|
||||||
|
@ -66,14 +66,14 @@ namespace Connector_HTTP {
|
||||||
afrt.setTimeScale(1000);
|
afrt.setTimeScale(1000);
|
||||||
//afrt.setQualityEntry(empty, 0);
|
//afrt.setQualityEntry(empty, 0);
|
||||||
MP4::afrt_runtable afrtrun;
|
MP4::afrt_runtable afrtrun;
|
||||||
long long int currTime = 0;
|
int i = 0;
|
||||||
for (int i = 0; i < metadata["keys"].size(); i++){
|
for (std::deque<DTSC::Key>::iterator it = trackMeta.keys.begin(); it != trackMeta.keys.end(); it++){
|
||||||
if (metadata["keys"][i]["len"].asInt() > 0){
|
if (it->getLength()){
|
||||||
afrtrun.firstFragment = metadata["keys"][i]["num"].asInt();
|
afrtrun.firstFragment = it->getNumber();
|
||||||
afrtrun.firstTimestamp = metadata["keys"][i]["time"].asInt();
|
afrtrun.firstTimestamp = it->getTime();
|
||||||
afrtrun.duration = metadata["keys"][i]["len"].asInt();
|
afrtrun.duration = it->getLength();
|
||||||
currTime = afrtrun.firstTimestamp + afrtrun.duration;
|
|
||||||
afrt.setFragmentRun(afrtrun, i);
|
afrt.setFragmentRun(afrtrun, i);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ namespace Connector_HTTP {
|
||||||
abst.setUpdate(false);
|
abst.setUpdate(false);
|
||||||
abst.setTimeScale(1000);
|
abst.setTimeScale(1000);
|
||||||
abst.setLive(isLive);
|
abst.setLive(isLive);
|
||||||
abst.setCurrentMediaTime(metadata["lastms"].asInt());
|
abst.setCurrentMediaTime(trackMeta.lastms);
|
||||||
abst.setSmpteTimeCodeOffset(0);
|
abst.setSmpteTimeCodeOffset(0);
|
||||||
abst.setMovieIdentifier(streamName);
|
abst.setMovieIdentifier(streamName);
|
||||||
abst.setSegmentRunTable(asrt, 0);
|
abst.setSegmentRunTable(asrt, 0);
|
||||||
|
@ -100,7 +100,7 @@ namespace Connector_HTTP {
|
||||||
///\param streamName The name of the stream.
|
///\param streamName The name of the stream.
|
||||||
///\param metadata The current metadata, used to generate the index.
|
///\param metadata The current metadata, used to generate the index.
|
||||||
///\return The index file for HTTP Dynamic Streaming.
|
///\return The index file for HTTP Dynamic Streaming.
|
||||||
std::string dynamicIndex(std::string & streamName, JSON::Value & metadata){
|
std::string dynamicIndex(std::string & streamName, DTSC::Meta & metadata){
|
||||||
if ( !audioTrack){getTracks(metadata);}
|
if ( !audioTrack){getTracks(metadata);}
|
||||||
std::stringstream Result;
|
std::stringstream Result;
|
||||||
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
|
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
|
||||||
|
@ -108,27 +108,27 @@ namespace Connector_HTTP {
|
||||||
Result << " <id>" << streamName << "</id>" << std::endl;
|
Result << " <id>" << streamName << "</id>" << std::endl;
|
||||||
Result << " <mimeType>video/mp4</mimeType>" << std::endl;
|
Result << " <mimeType>video/mp4</mimeType>" << std::endl;
|
||||||
Result << " <deliveryType>streaming</deliveryType>" << std::endl;
|
Result << " <deliveryType>streaming</deliveryType>" << std::endl;
|
||||||
if (metadata.isMember("vod")){
|
if (metadata.vod){
|
||||||
Result << " <duration>" << metadata["length"].asInt() << ".000</duration>" << std::endl;
|
Result << " <duration>" << metadata.tracks[*videoTracks.begin()].lastms / 1000 << ".000</duration>" << std::endl;
|
||||||
Result << " <streamType>recorded</streamType>" << std::endl;
|
Result << " <streamType>recorded</streamType>" << std::endl;
|
||||||
}else{
|
}else{
|
||||||
Result << " <duration>0.00</duration>" << std::endl;
|
Result << " <duration>0.00</duration>" << std::endl;
|
||||||
Result << " <streamType>live</streamType>" << std::endl;
|
Result << " <streamType>live</streamType>" << std::endl;
|
||||||
}
|
}
|
||||||
for (std::set<std::string>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
for (std::set<int>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
||||||
Result << " <bootstrapInfo "
|
Result << " <bootstrapInfo "
|
||||||
"profile=\"named\" "
|
"profile=\"named\" "
|
||||||
"id=\"boot" << metadata["tracks"][(*it)]["trackid"].asInt() << "\" "
|
"id=\"boot" << (*it) << "\" "
|
||||||
"url=\"" << metadata["tracks"][(*it)]["trackid"].asInt() << ".abst\">"
|
"url=\"" << (*it) << ".abst\">"
|
||||||
"</bootstrapInfo>" << std::endl;
|
"</bootstrapInfo>" << std::endl;
|
||||||
}
|
}
|
||||||
for (std::set<std::string>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
for (std::set<int>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
||||||
Result << " <media "
|
Result << " <media "
|
||||||
"url=\"" << metadata["tracks"][(*it)]["trackid"].asInt() << "-\" "
|
"url=\"" << (*it) << "-\" "
|
||||||
"bitrate=\"" << metadata["tracks"][(*it)]["bps"].asInt() * 8 << "\" "
|
"bitrate=\"" << metadata.tracks[(*it)].bps * 8 << "\" "
|
||||||
"bootstrapInfoId=\"boot" << metadata["tracks"][(*it)]["trackid"].asInt() << "\" "
|
"bootstrapInfoId=\"boot" << (*it) << "\" "
|
||||||
"width=\"" << metadata["tracks"][(*it)]["width"].asInt() << "\" "
|
"width=\"" << metadata.tracks[(*it)].width << "\" "
|
||||||
"height=\"" << metadata["tracks"][(*it)]["height"].asInt() << "\">" << std::endl;
|
"height=\"" << metadata.tracks[(*it)].height << "\">" << std::endl;
|
||||||
Result << " <metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>" << std::endl;
|
Result << " <metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>" << std::endl;
|
||||||
Result << " </media>" << std::endl;
|
Result << " </media>" << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ namespace Connector_HTTP {
|
||||||
std::string streamID = HTTP_R.url.substr(streamname.size() + 10);
|
std::string streamID = HTTP_R.url.substr(streamname.size() + 10);
|
||||||
streamID = streamID.substr(0, streamID.find(".abst"));
|
streamID = streamID.substr(0, streamID.find(".abst"));
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.getTrackById(atoll(streamID.c_str())),Strm.metadata.isMember("live")));
|
HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.metadata.tracks[atoll(streamID.c_str())], Strm.metadata.live));
|
||||||
HTTP_S.SetHeader("Content-Type", "binary/octet");
|
HTTP_S.SetHeader("Content-Type", "binary/octet");
|
||||||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||||
HTTP_S.SendResponse("200", "OK", conn);
|
HTTP_S.SendResponse("200", "OK", conn);
|
||||||
|
@ -202,32 +202,30 @@ namespace Connector_HTTP {
|
||||||
printf("Video track %d, segment %d, fragment %d\n", Quality, Segment, ReqFragment);
|
printf("Video track %d, segment %d, fragment %d\n", Quality, Segment, ReqFragment);
|
||||||
#endif
|
#endif
|
||||||
if (!audioTrack){getTracks(Strm.metadata);}
|
if (!audioTrack){getTracks(Strm.metadata);}
|
||||||
JSON::Value & vidTrack = Strm.getTrackById(Quality);
|
DTSC::Track & vidTrack = Strm.metadata.tracks[Quality];
|
||||||
mstime = 0;
|
mstime = 0;
|
||||||
mslen = 0;
|
mslen = 0;
|
||||||
if (vidTrack.isMember("keys")){
|
for (std::deque<DTSC::Key>::iterator it = vidTrack.keys.begin(); it != vidTrack.keys.end(); it++){
|
||||||
for (JSON::ArrIter it = vidTrack["keys"].ArrBegin(); it != vidTrack["keys"].ArrEnd(); it++){
|
if (it->getNumber() >= ReqFragment){
|
||||||
if ((*it)["num"].asInt() >= ReqFragment){
|
mstime = it->getTime();
|
||||||
mstime = (*it)["time"].asInt();
|
mslen = it->getLength();
|
||||||
mslen = (*it)["len"].asInt();
|
if (Strm.metadata.live){
|
||||||
if (Strm.metadata.isMember("live")){
|
if (it == vidTrack.keys.end() - 2){
|
||||||
if (it == vidTrack["keys"].ArrEnd() - 2){
|
HTTP_S.Clean();
|
||||||
HTTP_S.Clean();
|
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
HTTP_S.SendResponse("208", "Ask again later", conn);
|
||||||
HTTP_S.SendResponse("208", "Ask again later", conn);
|
HTTP_R.Clean(); //clean for any possible next requests
|
||||||
HTTP_R.Clean(); //clean for any possible next requests
|
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
|
||||||
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
|
if (ss.spool()){
|
||||||
if (ss.spool()){
|
while (Strm.parsePacket(ss.Received())){}
|
||||||
while (Strm.parsePacket(ss.Received())){}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
|
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
|
||||||
if (Strm.metadata.isMember("live")){
|
if (Strm.metadata.live){
|
||||||
if (mstime == 0 && ReqFragment > 1){
|
if (mstime == 0 && ReqFragment > 1){
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
||||||
|
@ -245,18 +243,18 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
||||||
HTTP_S.StartResponse(HTTP_R, conn);
|
HTTP_S.StartResponse(HTTP_R, conn);
|
||||||
//send the bootstrap
|
//send the bootstrap
|
||||||
std::string bootstrap = dynamicBootstrap(streamname, Strm.getTrackById(Quality), Strm.metadata.isMember("live"), ReqFragment);
|
std::string bootstrap = dynamicBootstrap(streamname, Strm.metadata.tracks[Quality], Strm.metadata.live, ReqFragment);
|
||||||
HTTP_S.Chunkify(bootstrap, conn);
|
HTTP_S.Chunkify(bootstrap, conn);
|
||||||
//send a zero-size mdat, meaning it stretches until end of file.
|
//send a zero-size mdat, meaning it stretches until end of file.
|
||||||
HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn);
|
HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn);
|
||||||
//send init data, if needed.
|
//send init data, if needed.
|
||||||
if (audioTrack > 0 && Strm.getTrackById(audioTrack).isMember("init")){
|
if (audioTrack > 0){
|
||||||
tmp.DTSCAudioInit(Strm.getTrackById(audioTrack));
|
tmp.DTSCAudioInit(Strm.metadata.tracks[audioTrack]);
|
||||||
tmp.tagTime(mstime);
|
tmp.tagTime(mstime);
|
||||||
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
|
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
|
||||||
}
|
}
|
||||||
if (Quality > 0 && Strm.getTrackById(Quality).isMember("init")){
|
if (Quality > 0){
|
||||||
tmp.DTSCVideoInit(Strm.getTrackById(Quality));
|
tmp.DTSCVideoInit(Strm.metadata.tracks[Quality]);
|
||||||
tmp.tagTime(mstime);
|
tmp.tagTime(mstime);
|
||||||
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
|
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,11 +121,13 @@ namespace Connector_HTTP {
|
||||||
std::stringstream cmd;
|
std::stringstream cmd;
|
||||||
cmd << "t";
|
cmd << "t";
|
||||||
|
|
||||||
if (Strm.metadata["tracks"].size()){
|
int tid = -1;
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if ( objIt->second["type"].asStringRef() == "meta" ){
|
if (it->second.type == "meta" ){
|
||||||
cmd << " " << objIt->second["trackid"].asInt();
|
if (tid == -1){
|
||||||
|
tid = it->second.trackID;
|
||||||
}
|
}
|
||||||
|
cmd << " " << it->second.trackID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +136,7 @@ namespace Connector_HTTP {
|
||||||
cmd.clear();
|
cmd.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxTime = Strm.metadata["lastms"].asInt();
|
int maxTime = Strm.metadata.tracks[tid].lastms;
|
||||||
|
|
||||||
cmd << "\ns " << seek_sec << "\np " << maxTime << "\n";
|
cmd << "\ns " << seek_sec << "\np " << maxTime << "\n";
|
||||||
ss.SendNow(cmd.str().c_str(), cmd.str().size());
|
ss.SendNow(cmd.str().c_str(), cmd.str().size());
|
||||||
|
|
|
@ -28,62 +28,65 @@ namespace Connector_HTTP {
|
||||||
///\brief Builds an index file for HTTP Live streaming.
|
///\brief Builds an index file for HTTP Live streaming.
|
||||||
///\param metadata The current metadata, used to generate the index.
|
///\param metadata The current metadata, used to generate the index.
|
||||||
///\return The index file for HTTP Live Streaming.
|
///\return The index file for HTTP Live Streaming.
|
||||||
std::string liveIndex(JSON::Value & metadata, bool isLive){
|
std::string liveIndex(DTSC::Meta & metadata, bool isLive){
|
||||||
std::stringstream result;
|
std::stringstream result;
|
||||||
if (metadata.isMember("tracks")){
|
result << "#EXTM3U\r\n";
|
||||||
result << "#EXTM3U\r\n";
|
int audioId = -1;
|
||||||
int audioId = -1;
|
std::string audioName;
|
||||||
std::string audioName;
|
bool defAudio = false;//set default audio track;
|
||||||
bool defAudio = false;//set default audio track;
|
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||||
for (JSON::ObjIter trackIt = metadata["tracks"].ObjBegin(); trackIt != metadata["tracks"].ObjEnd(); trackIt++){
|
if (it->second.type == "audio"){
|
||||||
if (trackIt->second["type"].asStringRef() == "audio"){
|
audioId = it->first;
|
||||||
audioId = trackIt->second["trackid"].asInt();
|
audioName = it->second.getIdentifier();
|
||||||
audioName = trackIt->first;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (JSON::ObjIter trackIt = metadata["tracks"].ObjBegin(); trackIt != metadata["tracks"].ObjEnd(); trackIt++){
|
}
|
||||||
if (trackIt->second["type"].asStringRef() == "video"){
|
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||||
int bWidth = trackIt->second["maxbps"].asInt();
|
if (it->second.type == "video"){
|
||||||
if (audioId != -1){
|
int bWidth = it->second.bps * 2;
|
||||||
bWidth += (metadata["tracks"][audioName]["maxbps"].asInt() * 2);
|
if (audioId != -1){
|
||||||
}
|
bWidth += metadata.tracks[audioId].bps * 2;
|
||||||
result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n";
|
|
||||||
result << trackIt->second["trackid"].asInt();
|
|
||||||
if (audioId != -1){
|
|
||||||
result << "_" << audioId;
|
|
||||||
}
|
|
||||||
result << "/index.m3u8\r\n";
|
|
||||||
}
|
}
|
||||||
|
result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n";
|
||||||
|
result << it->first;
|
||||||
|
if (audioId != -1){
|
||||||
|
result << "_" << audioId;
|
||||||
|
}
|
||||||
|
result << "/index.m3u8\r\n";
|
||||||
}
|
}
|
||||||
}else{
|
}
|
||||||
//parse single track
|
#if DEBUG >= 8
|
||||||
int longestFragment = 0;
|
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
|
||||||
for (JSON::ArrIter ai = metadata["frags"].ArrBegin(); ai != metadata["frags"].ArrEnd(); ai++){
|
#endif
|
||||||
if ((*ai)["dur"].asInt() > longestFragment){
|
return result.str();
|
||||||
longestFragment = (*ai)["dur"].asInt();
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
result << "#EXTM3U\r\n"
|
|
||||||
"#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n"
|
|
||||||
"#EXT-X-MEDIA-SEQUENCE:" << metadata["missed_frags"].asInt() << "\r\n";
|
|
||||||
for (JSON::ArrIter ai = metadata["frags"].ArrBegin(); ai != metadata["frags"].ArrEnd(); ai++){
|
|
||||||
long long int starttime = 0;
|
|
||||||
JSON::ArrIter fi = metadata["keys"].ArrBegin();
|
|
||||||
while (fi != metadata["keys"].ArrEnd() && (*fi)["num"].asInt() < (*ai)["num"].asInt()){
|
|
||||||
fi++;
|
|
||||||
}
|
|
||||||
if (fi != metadata["keys"].ArrEnd()){
|
|
||||||
starttime = (*fi)["time"].asInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
result << "#EXTINF:" << (((*ai)["dur"].asInt() + 500) / 1000) << ", no desc\r\n"
|
std::string liveIndex(DTSC::Track & metadata, bool isLive){
|
||||||
<< starttime << "_" << (*ai)["dur"].asInt() + starttime << ".ts\r\n";
|
std::stringstream result;
|
||||||
|
//parse single track
|
||||||
|
int longestFragment = 0;
|
||||||
|
for (std::deque<DTSC::Fragment>::iterator it = metadata.fragments.begin(); (it + 1) != metadata.fragments.end(); it++){
|
||||||
|
if (it->getDuration() > longestFragment){
|
||||||
|
longestFragment = it->getDuration();
|
||||||
}
|
}
|
||||||
if ( !isLive){
|
}
|
||||||
result << "#EXT-X-ENDLIST\r\n";
|
result << "#EXTM3U\r\n"
|
||||||
|
"#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n"
|
||||||
|
"#EXT-X-MEDIA-SEQUENCE:" << metadata.missedFrags << "\r\n";
|
||||||
|
for (std::deque<DTSC::Fragment>::iterator it = metadata.fragments.begin(); it != metadata.fragments.end(); it++){
|
||||||
|
long long int starttime = metadata.getKey(it->getNumber()).getTime();
|
||||||
|
|
||||||
|
if (it != (metadata.fragments.end() - 1)){
|
||||||
|
result << "#EXTINF:" << ((it->getDuration() + 500) / 1000) << ", no desc\r\n"
|
||||||
|
<< starttime << "_" << it->getDuration() + starttime << ".ts\r\n";
|
||||||
|
}else{
|
||||||
|
result << "#EXTINF:" << ((metadata.lastms - starttime + 500) / 1000) << ", no desc\r\n"
|
||||||
|
<< starttime << "_" << metadata.lastms << ".ts\r\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( !isLive){
|
||||||
|
result << "#EXT-X-ENDLIST\r\n";
|
||||||
|
}
|
||||||
#if DEBUG >= 8
|
#if DEBUG >= 8
|
||||||
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
|
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
@ -161,7 +164,7 @@ namespace Connector_HTTP {
|
||||||
lastVid = Segment * 90;
|
lastVid = Segment * 90;
|
||||||
temp = HTTP_R.url.find("_", temp) + 1;
|
temp = HTTP_R.url.find("_", temp) + 1;
|
||||||
int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str());
|
int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str());
|
||||||
if (Strm.metadata.isMember("live")){
|
if (Strm.metadata.live){
|
||||||
int seekable = Strm.canSeekms(Segment);
|
int seekable = Strm.canSeekms(Segment);
|
||||||
if (seekable < 0){
|
if (seekable < 0){
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
|
@ -207,10 +210,10 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||||
std::string manifest;
|
std::string manifest;
|
||||||
if (request.find("/") == std::string::npos){
|
if (request.find("/") == std::string::npos){
|
||||||
manifest = liveIndex(Strm.metadata, Strm.metadata.isMember("live"));
|
manifest = liveIndex(Strm.metadata, Strm.metadata.live);
|
||||||
}else{
|
}else{
|
||||||
int selectId = atoi(request.substr(0,request.find("/")).c_str());
|
int selectId = atoi(request.substr(0,request.find("/")).c_str());
|
||||||
manifest = liveIndex(Strm.getTrackById(selectId), Strm.metadata.isMember("live"));
|
manifest = liveIndex(Strm.metadata.tracks[selectId], Strm.metadata.live);
|
||||||
}
|
}
|
||||||
HTTP_S.SetBody(manifest);
|
HTTP_S.SetBody(manifest);
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||||
|
@ -235,7 +238,7 @@ namespace Connector_HTTP {
|
||||||
handlingRequest = false;
|
handlingRequest = false;
|
||||||
}
|
}
|
||||||
if ( !haveAvcc){
|
if ( !haveAvcc){
|
||||||
avccbox.setPayload(Strm.getTrackById(trackID)["init"].asString());
|
avccbox.setPayload(Strm.metadata.tracks[trackID].init);
|
||||||
haveAvcc = true;
|
haveAvcc = true;
|
||||||
}
|
}
|
||||||
if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
|
if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
|
||||||
|
@ -269,10 +272,10 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToPack.prepend(TS::Packet::getPESVideoLeadIn(0ul, Strm.getPacket()["time"].asInt() * 90));
|
ToPack.prepend(TS::Packet::getPESVideoLeadIn(0ul, Strm.getPacket()["time"].asInt() * 90));
|
||||||
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
||||||
ContCounter = &VideoCounter;
|
ContCounter = &VideoCounter;
|
||||||
}else if (Strm.lastType() == DTSC::AUDIO){
|
}else if (Strm.lastType() == DTSC::AUDIO){
|
||||||
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.getTrackById(audioTrackID)["init"].asString()));
|
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata.tracks[audioTrackID].init));
|
||||||
ToPack.append(Strm.lastData());
|
ToPack.append(Strm.lastData());
|
||||||
if (AppleCompat){
|
if (AppleCompat){
|
||||||
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), lastVid));
|
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), lastVid));
|
||||||
|
|
|
@ -101,19 +101,19 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
Strm.waitForMeta(ss);
|
Strm.waitForMeta(ss);
|
||||||
int byterate = 0;
|
int byterate = 0;
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (videoID == -1 && objIt->second["type"].asString() == "video"){
|
if (videoID == -1 && it->second.type == "video"){
|
||||||
videoID = objIt->second["trackid"].asInt();
|
videoID = it->second.trackID;
|
||||||
}
|
}
|
||||||
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
|
if (audioID == -1 && it->second.type == "audio"){
|
||||||
audioID = objIt->second["trackid"].asInt();
|
audioID = it->second.trackID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (videoID != -1){
|
if (videoID != -1){
|
||||||
byterate += Strm.getTrackById(videoID)["bps"].asInt();
|
byterate += Strm.metadata.tracks[videoID].bps;
|
||||||
}
|
}
|
||||||
if (audioID != -1){
|
if (audioID != -1){
|
||||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
byterate += Strm.metadata.tracks[audioID].bps;
|
||||||
}
|
}
|
||||||
if ( !byterate){byterate = 1;}
|
if ( !byterate){byterate = 1;}
|
||||||
if (seek_byte){
|
if (seek_byte){
|
||||||
|
@ -146,16 +146,16 @@ namespace Connector_HTTP {
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
||||||
conn.SendNow(FLV::Header, 13); //write FLV header
|
conn.SendNow(FLV::Header, 13); //write FLV header
|
||||||
//write metadata
|
//write metadata
|
||||||
tag.DTSCMetaInit(Strm, Strm.getTrackById(videoID), Strm.getTrackById(audioID));
|
tag.DTSCMetaInit(Strm, Strm.metadata.tracks[videoID], Strm.metadata.tracks[audioID]);
|
||||||
conn.SendNow(tag.data, tag.len);
|
conn.SendNow(tag.data, tag.len);
|
||||||
//write video init data, if needed
|
//write video init data, if needed
|
||||||
if (videoID != -1 && Strm.getTrackById(videoID).isMember("init")){
|
if (videoID != -1){
|
||||||
tag.DTSCVideoInit(Strm.getTrackById(videoID));
|
tag.DTSCVideoInit(Strm.metadata.tracks[videoID]);
|
||||||
conn.SendNow(tag.data, tag.len);
|
conn.SendNow(tag.data, tag.len);
|
||||||
}
|
}
|
||||||
//write audio init data, if needed
|
//write audio init data, if needed
|
||||||
if (audioID != -1 && Strm.getTrackById(audioID).isMember("init")){
|
if (audioID != -1){
|
||||||
tag.DTSCAudioInit(Strm.getTrackById(audioID));
|
tag.DTSCAudioInit(Strm.metadata.tracks[audioID]);
|
||||||
conn.SendNow(tag.data, tag.len);
|
conn.SendNow(tag.data, tag.len);
|
||||||
}
|
}
|
||||||
progressive_has_sent_header = true;
|
progressive_has_sent_header = true;
|
||||||
|
@ -170,7 +170,7 @@ namespace Connector_HTTP {
|
||||||
conn.close();
|
conn.close();
|
||||||
}
|
}
|
||||||
if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){
|
if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){
|
||||||
std::string codec = Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["codec"].asString();
|
std::string codec = Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].codec;
|
||||||
if (codec == "AAC" || codec == "MP3" || codec == "H264" || codec == "H263" || codec == "VP6"){
|
if (codec == "AAC" || codec == "MP3" || codec == "H264" || codec == "H263" || codec == "VP6"){
|
||||||
tag.DTSCLoader(Strm);
|
tag.DTSCLoader(Strm);
|
||||||
conn.SendNow(tag.data, tag.len); //write the tag contents
|
conn.SendNow(tag.data, tag.len); //write the tag contents
|
||||||
|
|
|
@ -96,13 +96,13 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
Strm.waitForMeta(ss);
|
Strm.waitForMeta(ss);
|
||||||
int byterate = 0;
|
int byterate = 0;
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (audioID == -1 && objIt->second["codec"].asString() == "MP3"){
|
if (audioID == -1 && it->second.codec == "MP3"){
|
||||||
audioID = objIt->second["trackid"].asInt();
|
audioID = it->second.trackID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (audioID != -1){
|
if (audioID != -1){
|
||||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
byterate += Strm.metadata.tracks[audioID].bps;
|
||||||
}
|
}
|
||||||
if ( !byterate){byterate = 1;}
|
if ( !byterate){byterate = 1;}
|
||||||
if (seek_byte){
|
if (seek_byte){
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetHeader("Content-Type", "video/MP4"); //Send the correct content-type for FLV files
|
HTTP_S.SetHeader("Content-Type", "video/MP4"); //Send the correct content-type for FLV files
|
||||||
HTTP_S.protocol = "HTTP/1.0";
|
HTTP_S.protocol = "HTTP/1.0";
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
||||||
conn.SendNow(Conv.DTSCMeta2MP4Header(Strm.metadata));//SENDING MP4HEADER
|
conn.SendNow(Conv.DTSCMeta2MP4Header(Strm.metadata.toJSON()));//SENDING MP4HEADER
|
||||||
keyPartIt = Conv.keyParts.begin();
|
keyPartIt = Conv.keyParts.begin();
|
||||||
{//using scope to have cmd not declared after action
|
{//using scope to have cmd not declared after action
|
||||||
std::stringstream cmd;
|
std::stringstream cmd;
|
||||||
|
@ -127,19 +127,19 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int byterate = 0;
|
int byterate = 0;
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (videoID == -1 && objIt->second["type"].asString() == "video"){
|
if (videoID == -1 && it->second.type == "video"){
|
||||||
videoID = objIt->second["trackid"].asInt();
|
videoID = it->second.trackID;
|
||||||
}
|
}
|
||||||
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
|
if (audioID == -1 && it->second.type == "audio"){
|
||||||
audioID = objIt->second["trackid"].asInt();
|
audioID = it->second.trackID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (videoID != -1){
|
if (videoID != -1){
|
||||||
byterate += Strm.getTrackById(videoID)["bps"].asInt();
|
byterate += Strm.metadata.tracks[videoID].bps;
|
||||||
}
|
}
|
||||||
if (audioID != -1){
|
if (audioID != -1){
|
||||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
byterate += Strm.metadata.tracks[audioID].bps;
|
||||||
}
|
}
|
||||||
if ( !byterate){byterate = 1;}
|
if ( !byterate){byterate = 1;}
|
||||||
seek_sec = (seek_byte / byterate) * 1000;
|
seek_sec = (seek_byte / byterate) * 1000;
|
||||||
|
|
|
@ -112,19 +112,19 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
Strm.waitForMeta(ss);
|
Strm.waitForMeta(ss);
|
||||||
int byterate = 0;
|
int byterate = 0;
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (videoID == -1 && objIt->second["codec"].asString() == "theora"){
|
if (videoID == -1 && it->second.codec == "theora"){
|
||||||
videoID = objIt->second["trackid"].asInt();
|
videoID = it->second.trackID;
|
||||||
}
|
}
|
||||||
if (audioID == -1 && objIt->second["codec"].asString() == "vorbis"){
|
if (audioID == -1 && it->second.codec == "vorbis"){
|
||||||
audioID = objIt->second["trackid"].asInt();
|
audioID = it->second.trackID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (videoID != -1){
|
if (videoID != -1){
|
||||||
byterate += Strm.getTrackById(videoID)["bps"].asInt();
|
byterate += Strm.metadata.tracks[videoID].bps;
|
||||||
}
|
}
|
||||||
if (audioID != -1){
|
if (audioID != -1){
|
||||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
byterate += Strm.metadata.tracks[audioID].bps;
|
||||||
}
|
}
|
||||||
if ( !byterate){byterate = 1;}
|
if ( !byterate){byterate = 1;}
|
||||||
if (seek_byte){
|
if (seek_byte){
|
||||||
|
@ -163,7 +163,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.protocol = "HTTP/1.0";
|
HTTP_S.protocol = "HTTP/1.0";
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
||||||
//Fill in ogg header here
|
//Fill in ogg header here
|
||||||
oggMeta.readDTSCHeader(Strm.metadata);
|
oggMeta.readDTSCHeader(Strm.metadata.toJSON());
|
||||||
conn.SendNow((char*)oggMeta.parsedPages.c_str(), oggMeta.parsedPages.size());
|
conn.SendNow((char*)oggMeta.parsedPages.c_str(), oggMeta.parsedPages.size());
|
||||||
progressive_has_sent_header = true;
|
progressive_has_sent_header = true;
|
||||||
//setting sendReady to not ready
|
//setting sendReady to not ready
|
||||||
|
|
|
@ -30,42 +30,42 @@ namespace Connector_HTTP {
|
||||||
///\brief Builds an index file for HTTP Smooth streaming.
|
///\brief Builds an index file for HTTP Smooth streaming.
|
||||||
///\param metadata The current metadata, used to generate the index.
|
///\param metadata The current metadata, used to generate the index.
|
||||||
///\return The index file for HTTP Smooth Streaming.
|
///\return The index file for HTTP Smooth Streaming.
|
||||||
std::string smoothIndex(JSON::Value & metadata){
|
std::string smoothIndex(DTSC::Meta & metadata){
|
||||||
std::stringstream Result;
|
std::stringstream Result;
|
||||||
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||||
Result << "<SmoothStreamingMedia "
|
Result << "<SmoothStreamingMedia "
|
||||||
"MajorVersion=\"2\" "
|
"MajorVersion=\"2\" "
|
||||||
"MinorVersion=\"0\" "
|
"MinorVersion=\"0\" "
|
||||||
"TimeScale=\"10000000\" ";
|
"TimeScale=\"10000000\" ";
|
||||||
if (metadata.isMember("vod")){
|
std::map<int,DTSC::Track> allAudio;
|
||||||
Result << "Duration=\"" << metadata["lastms"].asInt() << "0000\"";
|
std::map<int,DTSC::Track> allVideo;
|
||||||
}else{
|
|
||||||
Result << "Duration=\"0\" "
|
|
||||||
"IsLive=\"TRUE\" "
|
|
||||||
"LookAheadFragmentCount=\"2\" "
|
|
||||||
"DVRWindowLength=\"" + metadata["buffer_window"].asString() + "0000\" "
|
|
||||||
"CanSeek=\"TRUE\" "
|
|
||||||
"CanPause=\"TRUE\" ";
|
|
||||||
}
|
|
||||||
Result << ">\n";
|
|
||||||
JSON::Value allAudio;
|
|
||||||
JSON::Value allVideo;
|
|
||||||
long long int maxWidth = 0;
|
long long int maxWidth = 0;
|
||||||
long long int maxHeight = 0;
|
long long int maxHeight = 0;
|
||||||
long long int minWidth = 99999999;
|
long long int minWidth = 99999999;
|
||||||
long long int minHeight = 99999999;
|
long long int minHeight = 99999999;
|
||||||
for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){
|
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||||
if (oIt->second["type"].asString() == "audio" && oIt->second["codec"].asString() == "AAC"){
|
if (it->second.type == "audio" && it->second.codec == "AAC"){
|
||||||
allAudio[oIt->first] = oIt->second;
|
allAudio.insert(*it);
|
||||||
}
|
}
|
||||||
if (oIt->second["type"].asString() == "video" && oIt->second["codec"].asString() == "H264"){
|
if (it->second.type == "video" && it->second.codec == "H264"){
|
||||||
allVideo[oIt->first] = oIt->second;
|
allVideo.insert(*it);
|
||||||
if (oIt->second["width"].asInt() > maxWidth){maxWidth = oIt->second["width"].asInt();}
|
if (it->second.width > maxWidth){maxWidth = it->second.width;}
|
||||||
if (oIt->second["width"].asInt() < minWidth){minWidth = oIt->second["width"].asInt();}
|
if (it->second.width < minWidth){minWidth = it->second.width;}
|
||||||
if (oIt->second["height"].asInt() > maxHeight){maxHeight = oIt->second["height"].asInt();}
|
if (it->second.height > maxHeight){maxHeight = it->second.height;}
|
||||||
if (oIt->second["height"].asInt() < minHeight){minHeight = oIt->second["height"].asInt();}
|
if (it->second.height < minHeight){minHeight = it->second.height;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (metadata.vod){
|
||||||
|
Result << "Duration=\"" << metadata.tracks[allVideo.begin()->first].lastms << "0000\"";
|
||||||
|
}else{
|
||||||
|
Result << "Duration=\"0\" "
|
||||||
|
"IsLive=\"TRUE\" "
|
||||||
|
"LookAheadFragmentCount=\"2\" "
|
||||||
|
"DVRWindowLength=\"" << metadata.bufferWindow << "0000\" "
|
||||||
|
"CanSeek=\"TRUE\" "
|
||||||
|
"CanPause=\"TRUE\" ";
|
||||||
|
}
|
||||||
|
Result << ">\n";
|
||||||
|
|
||||||
//Add audio entries
|
//Add audio entries
|
||||||
if (allAudio.size()){
|
if (allAudio.size()){
|
||||||
|
@ -73,36 +73,36 @@ namespace Connector_HTTP {
|
||||||
"Type=\"audio\" "
|
"Type=\"audio\" "
|
||||||
"QualityLevels=\"" << allAudio.size() << "\" "
|
"QualityLevels=\"" << allAudio.size() << "\" "
|
||||||
"Name=\"audio\" "
|
"Name=\"audio\" "
|
||||||
"Chunks=\"" << allAudio.ObjBegin()->second["keys"].size() << "\" "
|
"Chunks=\"" << allAudio.begin()->second.keys.size() << "\" "
|
||||||
"Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n";
|
"Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n";
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (JSON::ObjIter oIt = allAudio.ObjBegin(); oIt != allAudio.ObjEnd(); oIt++){
|
for (std::map<int,DTSC::Track>::iterator it = allAudio.begin(); it != allAudio.end(); it++){
|
||||||
Result << "<QualityLevel "
|
Result << "<QualityLevel "
|
||||||
"Index=\"" << index << "\" "
|
"Index=\"" << index << "\" "
|
||||||
"Bitrate=\"" << oIt->second["bps"].asInt() * 8 << "\" "
|
"Bitrate=\"" << it->second.bps * 8 << "\" "
|
||||||
"CodecPrivateData=\"" << std::hex;
|
"CodecPrivateData=\"" << std::hex;
|
||||||
for (int i = 0; i < oIt->second["init"].asString().size(); i++){
|
for (int i = 0; i < it->second.init.size(); i++){
|
||||||
Result << std::setfill('0') << std::setw(2) << std::right << (int)oIt->second["init"].asString()[i];
|
Result << std::setfill('0') << std::setw(2) << std::right << (int)it->second.init[i];
|
||||||
}
|
}
|
||||||
Result << std::dec << "\" "
|
Result << std::dec << "\" "
|
||||||
"SamplingRate=\"" << oIt->second["rate"].asInt() << "\" "
|
"SamplingRate=\"" << it->second.rate << "\" "
|
||||||
"Channels=\"2\" "
|
"Channels=\"2\" "
|
||||||
"BitsPerSample=\"16\" "
|
"BitsPerSample=\"16\" "
|
||||||
"PacketSize=\"4\" "
|
"PacketSize=\"4\" "
|
||||||
"AudioTag=\"255\" "
|
"AudioTag=\"255\" "
|
||||||
"FourCC=\"AACL\" >\n";
|
"FourCC=\"AACL\" >\n";
|
||||||
Result << "<CustomAttributes>\n"
|
Result << "<CustomAttributes>\n"
|
||||||
"<Attribute Name = \"TrackID\" Value = \"" << oIt->second["trackid"].asString() << "\" />"
|
"<Attribute Name = \"TrackID\" Value = \"" << it->first << "\" />"
|
||||||
"</CustomAttributes>";
|
"</CustomAttributes>";
|
||||||
Result << "</QualityLevel>\n";
|
Result << "</QualityLevel>\n";
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
for (JSON::ArrIter keyIt = allAudio.ObjBegin()->second["keys"].ArrBegin(); keyIt != ((allAudio.ObjBegin()->second["keys"].ArrEnd()) - 1); keyIt++){
|
for (std::deque<DTSC::Key>::iterator it = allAudio.begin()->second.keys.begin(); it != ((allAudio.begin()->second.keys.end()) - 1); it++){
|
||||||
Result << "<c ";
|
Result << "<c ";
|
||||||
if (keyIt == allAudio.ObjBegin()->second["keys"].ArrBegin()){
|
if (it == allAudio.begin()->second.keys.begin()){
|
||||||
Result << "t=\"" << allAudio.ObjBegin()->second["firstms"].asInt() * 10000 << "\" ";
|
Result << "t=\"" << it->getTime() * 10000 << "\" ";
|
||||||
}
|
}
|
||||||
Result << "d=\"" << (*keyIt)["len"].asInt() * 10000 << "\" />\n";
|
Result << "d=\"" << it->getLength() * 10000 << "\" />\n";
|
||||||
}
|
}
|
||||||
Result << "</StreamIndex>\n";
|
Result << "</StreamIndex>\n";
|
||||||
}
|
}
|
||||||
|
@ -112,41 +112,41 @@ namespace Connector_HTTP {
|
||||||
"Type=\"video\" "
|
"Type=\"video\" "
|
||||||
"QualityLevels=\"" << allVideo.size() << "\" "
|
"QualityLevels=\"" << allVideo.size() << "\" "
|
||||||
"Name=\"video\" "
|
"Name=\"video\" "
|
||||||
"Chunks=\"" << allVideo.ObjBegin()->second["keys"].size() << "\" "
|
"Chunks=\"" << allVideo.begin()->second.keys.size() << "\" "
|
||||||
"Url=\"Q({bitrate},{CustomAttributes})/V({start time})\" "
|
"Url=\"Q({bitrate},{CustomAttributes})/V({start time})\" "
|
||||||
"MaxWidth=\"" << maxWidth << "\" "
|
"MaxWidth=\"" << maxWidth << "\" "
|
||||||
"MaxHeight=\"" << maxHeight << "\" "
|
"MaxHeight=\"" << maxHeight << "\" "
|
||||||
"DisplayWidth=\"" << maxWidth << "\" "
|
"DisplayWidth=\"" << maxWidth << "\" "
|
||||||
"DisplayHeight=\"" << maxHeight << "\">\n";
|
"DisplayHeight=\"" << maxHeight << "\">\n";
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (JSON::ObjIter oIt = allVideo.ObjBegin(); oIt != allVideo.ObjEnd(); oIt++){
|
for (std::map<int,DTSC::Track>::iterator it = allVideo.begin(); it != allVideo.end(); it++){
|
||||||
//Add video qualities
|
//Add video qualities
|
||||||
Result << "<QualityLevel "
|
Result << "<QualityLevel "
|
||||||
"Index=\"" << index << "\" "
|
"Index=\"" << index << "\" "
|
||||||
"Bitrate=\"" << oIt->second["bps"].asInt() * 8 << "\" "
|
"Bitrate=\"" << it->second.bps * 8 << "\" "
|
||||||
"CodecPrivateData=\"" << std::hex;
|
"CodecPrivateData=\"" << std::hex;
|
||||||
MP4::AVCC avccbox;
|
MP4::AVCC avccbox;
|
||||||
avccbox.setPayload(oIt->second["init"].asString());
|
avccbox.setPayload(it->second.init);
|
||||||
std::string tmpString = avccbox.asAnnexB();
|
std::string tmpString = avccbox.asAnnexB();
|
||||||
for (int i = 0; i < tmpString.size(); i++){
|
for (int i = 0; i < tmpString.size(); i++){
|
||||||
Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i];
|
Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i];
|
||||||
}
|
}
|
||||||
Result << std::dec << "\" "
|
Result << std::dec << "\" "
|
||||||
"MaxWidth=\"" << oIt->second["width"].asInt() << "\" "
|
"MaxWidth=\"" << it->second.width << "\" "
|
||||||
"MaxHeight=\"" << oIt->second["height"].asInt() << "\" "
|
"MaxHeight=\"" << it->second.height << "\" "
|
||||||
"FourCC=\"AVC1\" >\n";
|
"FourCC=\"AVC1\" >\n";
|
||||||
Result << "<CustomAttributes>\n"
|
Result << "<CustomAttributes>\n"
|
||||||
"<Attribute Name = \"TrackID\" Value = \"" << oIt->second["trackid"].asString() << "\" />"
|
"<Attribute Name = \"TrackID\" Value = \"" << it->first << "\" />"
|
||||||
"</CustomAttributes>";
|
"</CustomAttributes>";
|
||||||
Result << "</QualityLevel>\n";
|
Result << "</QualityLevel>\n";
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
for (JSON::ArrIter keyIt = allVideo.ObjBegin()->second["keys"].ArrBegin(); keyIt != ((allVideo.ObjBegin()->second["keys"].ArrEnd()) - 1); keyIt++){
|
for (std::deque<DTSC::Key>::iterator it = allVideo.begin()->second.keys.begin(); it != ((allVideo.begin()->second.keys.end()) - 1); it++){
|
||||||
Result << "<c ";
|
Result << "<c ";
|
||||||
if (keyIt == allVideo.ObjBegin()->second["keys"].ArrBegin()){
|
if (it == allVideo.begin()->second.keys.begin()){
|
||||||
Result << "t=\"" << (*keyIt)["time"].asInt() * 10000 << "\" ";
|
Result << "t=\"" << it->getTime() * 10000 << "\" ";
|
||||||
}
|
}
|
||||||
Result << "d=\"" << (*keyIt)["len"].asInt() * 10000 << "\" />\n";
|
Result << "d=\"" << it->getLength() * 10000 << "\" />\n";
|
||||||
}
|
}
|
||||||
Result << "</StreamIndex>\n";
|
Result << "</StreamIndex>\n";
|
||||||
}
|
}
|
||||||
|
@ -183,8 +183,8 @@ namespace Connector_HTTP {
|
||||||
unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
|
unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
|
||||||
conn.setBlocking(false);//Set the client socket to non-blocking
|
conn.setBlocking(false);//Set the client socket to non-blocking
|
||||||
|
|
||||||
JSON::Value allAudio;
|
std::map<int,DTSC::Track> allAudio;
|
||||||
JSON::Value allVideo;
|
std::map<int,DTSC::Track> allVideo;
|
||||||
|
|
||||||
while (conn.connected()){
|
while (conn.connected()){
|
||||||
if ( !handlingRequest){
|
if ( !handlingRequest){
|
||||||
|
@ -211,12 +211,12 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
ss.setBlocking(false);
|
ss.setBlocking(false);
|
||||||
Strm.waitForMeta(ss);
|
Strm.waitForMeta(ss);
|
||||||
for (JSON::ObjIter oIt = Strm.metadata["tracks"].ObjBegin(); oIt != Strm.metadata["tracks"].ObjEnd(); oIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (oIt->second["type"].asString() == "audio" && oIt->second["codec"].asString() == "AAC"){
|
if (it->second.type == "audio" && it->second.codec == "AAC"){
|
||||||
allAudio[oIt->first] = oIt->second;
|
allAudio[it->first] = it->second;
|
||||||
}
|
}
|
||||||
if (oIt->second["type"].asString() == "video" && oIt->second["codec"].asString() == "H264"){
|
if (it->second.type == "video" && it->second.codec == "H264"){
|
||||||
allVideo[oIt->first] = oIt->second;
|
allVideo[it->first] = it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -250,14 +250,14 @@ namespace Connector_HTTP {
|
||||||
parseString = parseString.substr(parseString.find("(") + 1);
|
parseString = parseString.substr(parseString.find("(") + 1);
|
||||||
requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str());
|
requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str());
|
||||||
long long int selectedQuality = atoll(Quality.c_str());
|
long long int selectedQuality = atoll(Quality.c_str());
|
||||||
JSON::Value & myRef = Strm.getTrackById(selectedQuality);
|
DTSC::Track & myRef = Strm.metadata.tracks[selectedQuality];
|
||||||
if (Strm.metadata.isMember("live")){
|
if (Strm.metadata.live){
|
||||||
int seekable = Strm.canSeekms(requestedTime / 10000);
|
int seekable = Strm.canSeekms(requestedTime / 10000);
|
||||||
if (seekable == 0){
|
if (seekable == 0){
|
||||||
// iff the fragment in question is available, check if the next is available too
|
// iff the fragment in question is available, check if the next is available too
|
||||||
for (JSON::ArrIter aIt = myRef["keys"].ArrBegin(); aIt != myRef["keys"].ArrEnd(); aIt++){
|
for (std::deque<DTSC::Key>::iterator it = myRef.keys.begin(); it != myRef.keys.end(); it++){
|
||||||
if ((*aIt)["time"].asInt() >= (requestedTime / 10000)){
|
if (it->getTime() >= (requestedTime / 10000)){
|
||||||
if ((aIt + 1) == myRef["keys"].ArrEnd()){
|
if ((it + 1) == myRef.keys.end()){
|
||||||
seekable = 1;
|
seekable = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -269,7 +269,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
||||||
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
|
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
|
||||||
HTTP_R.Clean(); //clean for any possible next requests
|
HTTP_R.Clean(); //clean for any possible next requests
|
||||||
std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << myRef["keys"][0u]["time"].asInt() << " - " << myRef["keys"][myRef["keys"].size() - 1]["time"].asInt() << " ms)" << std::endl;
|
std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << myRef.keys.begin()->getTime() << " - " << myRef.keys.rbegin()->getTime() << " ms)" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (seekable > 0){
|
if (seekable > 0){
|
||||||
|
@ -277,7 +277,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||||
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
||||||
HTTP_R.Clean(); //clean for any possible next requests
|
HTTP_R.Clean(); //clean for any possible next requests
|
||||||
std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << myRef["keys"][0u]["time"].asInt() << " - " << myRef["keys"][myRef["keys"].size() - 1]["time"].asInt() << " ms)" << std::endl;
|
std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << myRef.keys.begin()->getTime() << " - " << myRef.keys.rbegin()->getTime() << " ms)" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,26 +286,24 @@ namespace Connector_HTTP {
|
||||||
|
|
||||||
long long mstime = 0;
|
long long mstime = 0;
|
||||||
long long mslen = 0;
|
long long mslen = 0;
|
||||||
if (myRef.isMember("keys")){
|
for (std::deque<DTSC::Key>::iterator it = myRef.keys.end(); it != myRef.keys.end(); it++){
|
||||||
for (JSON::ArrIter it = myRef["keys"].ArrBegin(); it != myRef["keys"].ArrEnd(); it++){
|
if (it->getTime() >= (requestedTime / 10000)){
|
||||||
if ((*it)["time"].asInt() >= (requestedTime / 10000)){
|
mstime = it->getTime();
|
||||||
mstime = (*it)["time"].asInt();
|
mslen = it->getLength();
|
||||||
mslen = (*it)["len"].asInt();
|
if (Strm.metadata.live){
|
||||||
if (Strm.metadata.isMember("live")){
|
if (it == myRef.keys.end() - 2){
|
||||||
if (it == myRef["keys"].ArrEnd() - 2){
|
HTTP_S.Clean();
|
||||||
HTTP_S.Clean();
|
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
||||||
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
HTTP_R.Clean(); //clean for any possible next requests
|
||||||
HTTP_R.Clean(); //clean for any possible next requests
|
std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl;
|
||||||
std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
|
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
|
||||||
if (Strm.metadata.isMember("live")){
|
if (Strm.metadata.live){
|
||||||
if (mstime == 0 && (requestedTime / 10000) > 1){
|
if (mstime == 0 && (requestedTime / 10000) > 1){
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
||||||
|
@ -317,73 +315,83 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Obtain the corresponding track;
|
//Obtain the corresponding track;
|
||||||
JSON::Value trackRef;
|
DTSC::Track trackRef;
|
||||||
if (wantsVideo){
|
if (wantsVideo){
|
||||||
trackRef = allVideo.ObjBegin()->second;
|
trackRef = allVideo.begin()->second;
|
||||||
}
|
}
|
||||||
if (wantsAudio){
|
if (wantsAudio){
|
||||||
trackRef = allAudio.ObjBegin()->second;
|
trackRef = allAudio.begin()->second;
|
||||||
}
|
}
|
||||||
//Also obtain the associated keyframe;
|
//Also obtain the associated keyframe;
|
||||||
JSON::Value keyObj;
|
DTSC::Key keyObj;
|
||||||
for (JSON::ArrIter keyIt = trackRef["keys"].ArrBegin(); keyIt != trackRef["keys"].ArrEnd(); keyIt++){
|
int partOffset = 0;
|
||||||
if ((*keyIt)["time"].asInt() >= (requestedTime / 10000)){
|
int keyDur = 0;
|
||||||
|
for (std::deque<DTSC::Key>::iterator keyIt = trackRef.keys.begin(); keyIt != trackRef.keys.end(); keyIt++){
|
||||||
|
if (keyIt->getTime() >= (requestedTime / 10000)){
|
||||||
keyObj = (*keyIt);
|
keyObj = (*keyIt);
|
||||||
|
std::deque<DTSC::Key>::iterator nextIt = keyIt;
|
||||||
|
nextIt++;
|
||||||
|
if (nextIt != trackRef.keys.end()){
|
||||||
|
keyDur = nextIt->getTime() - keyIt->getTime();
|
||||||
|
}else{
|
||||||
|
keyDur = -1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
partOffset += keyIt->getParts();
|
||||||
}
|
}
|
||||||
|
|
||||||
sstream << "t " << myRef["trackid"].asInt() << "\n";
|
sstream << "t " << myRef.trackID << "\n";
|
||||||
sstream << "s " << keyObj["time"].asInt() << "\n";
|
sstream << "s " << keyObj.getTime() << "\n";
|
||||||
sstream << "p " << keyObj["time"].asInt() + keyObj["len"].asInt() << "\n";
|
if (keyDur != -1){
|
||||||
// sstream << "o\n";
|
sstream << "p " << keyObj.getTime() + keyDur << "\n";
|
||||||
|
}else{
|
||||||
|
sstream << "p\n";
|
||||||
|
}
|
||||||
|
|
||||||
ss.SendNow(sstream.str().c_str());
|
ss.SendNow(sstream.str().c_str());
|
||||||
|
std::cerr << "[[]]Requested " << sstream.str() << std::endl;
|
||||||
|
|
||||||
unsigned int myDuration;
|
unsigned int myDuration;
|
||||||
|
|
||||||
//Wrap everything in mp4 boxes
|
//Wrap everything in mp4 boxes
|
||||||
MP4::MFHD mfhd_box;
|
MP4::MFHD mfhd_box;
|
||||||
mfhd_box.setSequenceNumber(keyObj["num"].asInt());
|
mfhd_box.setSequenceNumber(keyObj.getNumber());
|
||||||
myDuration = keyObj["len"].asInt() * 10000;
|
myDuration = keyObj.getLength() * 10000;
|
||||||
|
|
||||||
MP4::TFHD tfhd_box;
|
MP4::TFHD tfhd_box;
|
||||||
tfhd_box.setFlags(MP4::tfhdSampleFlag);
|
tfhd_box.setFlags(MP4::tfhdSampleFlag);
|
||||||
tfhd_box.setTrackID(1);
|
tfhd_box.setTrackID(1);
|
||||||
//if (trackRef["type"].asString() == "video"){
|
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
|
||||||
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
|
|
||||||
//}else{
|
|
||||||
// tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noKeySample);
|
|
||||||
//}
|
|
||||||
|
|
||||||
MP4::TRUN trun_box;
|
MP4::TRUN trun_box;
|
||||||
trun_box.setDataOffset(42);
|
trun_box.setDataOffset(42);
|
||||||
if (trackRef["type"].asString() == "video"){
|
int keySize = 0;
|
||||||
|
if (trackRef.type == "video"){
|
||||||
trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize);
|
trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize);
|
||||||
}else{
|
}else{
|
||||||
trun_box.setFlags(MP4::trundataOffset | MP4::trunsampleDuration | MP4::trunsampleSize);
|
trun_box.setFlags(MP4::trundataOffset | MP4::trunsampleDuration | MP4::trunsampleSize);
|
||||||
// trun_box.setFirstSampleFlags(0x00000040 | MP4::noKeySample);
|
|
||||||
}
|
}
|
||||||
trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample);
|
trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample);
|
||||||
std::deque<long long int> tmpParts;
|
for (int i = 0; i < keyObj.getParts(); i++){
|
||||||
JSON::decodeVector(keyObj["parts"].asString(), tmpParts);
|
|
||||||
for (int i = 0; i < tmpParts.size(); i++){
|
|
||||||
MP4::trunSampleInformation trunSample;
|
MP4::trunSampleInformation trunSample;
|
||||||
trunSample.sampleSize = tmpParts[i];
|
trunSample.sampleSize = Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getSize();
|
||||||
|
keySize += Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getSize();
|
||||||
//Guesstimate sample duration.
|
//Guesstimate sample duration.
|
||||||
trunSample.sampleDuration = ((double)(keyObj["len"].asInt() * 10000) / tmpParts.size());
|
trunSample.sampleDuration = Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getDuration() * 10000;
|
||||||
trun_box.setSampleInformation(trunSample, i);
|
trun_box.setSampleInformation(trunSample, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
MP4::SDTP sdtp_box;
|
MP4::SDTP sdtp_box;
|
||||||
sdtp_box.setVersion(0);
|
sdtp_box.setVersion(0);
|
||||||
if (trackRef["type"].asString() == "video"){
|
if (trackRef.type == "video"){
|
||||||
sdtp_box.setValue(36, 4);
|
sdtp_box.setValue(36, 4);
|
||||||
for (int i = 1; i < keyObj["partsize"].asInt(); i++){
|
for (int i = 1; i < keyObj.getParts(); i++){
|
||||||
sdtp_box.setValue(20, 4 + i);
|
sdtp_box.setValue(20, 4 + i);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
sdtp_box.setValue(40, 4);
|
sdtp_box.setValue(40, 4);
|
||||||
for (int i = 1; i < keyObj["partsize"].asInt(); i++){
|
for (int i = 1; i < keyObj.getParts(); i++){
|
||||||
sdtp_box.setValue(40, 4 + i);
|
sdtp_box.setValue(40, 4 + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,22 +402,24 @@ namespace Connector_HTTP {
|
||||||
traf_box.setContent(sdtp_box, 2);
|
traf_box.setContent(sdtp_box, 2);
|
||||||
|
|
||||||
//If the stream is live, we want to have a fragref box if possible
|
//If the stream is live, we want to have a fragref box if possible
|
||||||
if (Strm.metadata.isMember("live")){
|
if (Strm.metadata.live){
|
||||||
///\todo Fix this for live
|
///\todo Fix this for live
|
||||||
MP4::UUID_TrackFragmentReference fragref_box;
|
MP4::UUID_TrackFragmentReference fragref_box;
|
||||||
fragref_box.setVersion(1);
|
fragref_box.setVersion(1);
|
||||||
fragref_box.setFragmentCount(0);
|
fragref_box.setFragmentCount(0);
|
||||||
int fragCount = 0;
|
int fragCount = 0;
|
||||||
for (int i = 0; i < 2 && i < trackRef["keys"].size() - 1; i++){//< trackRef["keys"].size() - 1; i++){
|
for (int i = 0; i < 2 && i < trackRef.keys.size() - 1; i++){//< trackRef["keys"].size() - 1; i++){
|
||||||
if (trackRef["keys"][i]["time"].asInt() > (requestedTime / 10000)){
|
if (trackRef.keys[i].getTime() > (requestedTime / 10000)){
|
||||||
fragref_box.setTime(fragCount, trackRef["keys"][i]["time"].asInt() * 10000);
|
fragref_box.setTime(fragCount, trackRef.keys[i].getTime() * 10000);
|
||||||
fragref_box.setDuration(fragCount, trackRef["keys"][i]["len"].asInt() * 10000);
|
fragref_box.setDuration(fragCount, trackRef.keys[i].getLength() * 10000);
|
||||||
fragref_box.setFragmentCount(++fragCount);
|
fragref_box.setFragmentCount(++fragCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
traf_box.setContent(fragref_box, 3);
|
traf_box.setContent(fragref_box, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MP4::MOOF moof_box;
|
MP4::MOOF moof_box;
|
||||||
moof_box.setContent(mfhd_box, 0);
|
moof_box.setContent(mfhd_box, 0);
|
||||||
moof_box.setContent(traf_box, 1);
|
moof_box.setContent(traf_box, 1);
|
||||||
|
@ -423,7 +433,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
||||||
HTTP_S.StartResponse(HTTP_R, conn);
|
HTTP_S.StartResponse(HTTP_R, conn);
|
||||||
HTTP_S.Chunkify(moof_box.asBox(), moof_box.boxedSize(), conn);
|
HTTP_S.Chunkify(moof_box.asBox(), moof_box.boxedSize(), conn);
|
||||||
int size = htonl(keyObj["size"].asInt() + 8);
|
int size = htonl(keySize + 8);
|
||||||
HTTP_S.Chunkify((char*)&size, 4, conn);
|
HTTP_S.Chunkify((char*)&size, 4, conn);
|
||||||
HTTP_S.Chunkify("mdat", 4, conn);
|
HTTP_S.Chunkify("mdat", 4, conn);
|
||||||
handlingRequest = true;
|
handlingRequest = true;
|
||||||
|
@ -465,6 +475,8 @@ namespace Connector_HTTP {
|
||||||
handlingRequest = false;
|
handlingRequest = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
Util::sleep(10);
|
||||||
}
|
}
|
||||||
if ( !ss.connected()){
|
if ( !ss.connected()){
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -107,23 +107,16 @@ namespace Connector_HTTP {
|
||||||
|
|
||||||
if(trackID == -1){
|
if(trackID == -1){
|
||||||
// no track was given. Fetch the first track that has SRT data
|
// no track was given. Fetch the first track that has SRT data
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (objIt->second["codec"].asStringRef() == "srt"){
|
if (it->second.codec == "srt"){
|
||||||
trackID = objIt->second["trackid"].asInt();
|
trackID = it->second.trackID;
|
||||||
subtitleTrack = true;
|
subtitleTrack = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
// track *was* given, but we have to check whether it's an actual srt track
|
// track *was* given, but we have to check whether it's an actual srt track
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
subtitleTrack = Strm.metadata.tracks[trackID].codec == "srt";
|
||||||
if (objIt->second["trackid"].asInt() == trackID){
|
|
||||||
subtitleTrack = (objIt->second["codec"].asStringRef() == "srt");
|
|
||||||
break;
|
|
||||||
}else{
|
|
||||||
subtitleTrack = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!subtitleTrack){
|
if(!subtitleTrack){
|
||||||
|
@ -139,7 +132,7 @@ namespace Connector_HTTP {
|
||||||
|
|
||||||
cmd << "t " << trackID;
|
cmd << "t " << trackID;
|
||||||
|
|
||||||
int maxTime = Strm.metadata["lastms"].asInt();
|
int maxTime = Strm.metadata.tracks[trackID].lastms;
|
||||||
|
|
||||||
cmd << "\ns " << seek_time << "\np " << maxTime << "\n";
|
cmd << "\ns " << seek_time << "\np " << maxTime << "\n";
|
||||||
ss.SendNow(cmd.str().c_str(), cmd.str().size());
|
ss.SendNow(cmd.str().c_str(), cmd.str().size());
|
||||||
|
|
|
@ -33,14 +33,14 @@ int main(int argc, char ** argv){
|
||||||
}
|
}
|
||||||
long long int lastStats = 0;
|
long long int lastStats = 0;
|
||||||
long long int started = Util::epoch();
|
long long int started = Util::epoch();
|
||||||
while (std::cout.good()){
|
while (std::cout.good() && S.connected()){
|
||||||
if (S.spool()){
|
if (S.spool()){
|
||||||
while (S.Received().size()){
|
while (S.Received().size()){
|
||||||
std::cout.write(S.Received().get().c_str(), S.Received().get().size());
|
std::cout.write(S.Received().get().c_str(), S.Received().get().size());
|
||||||
S.Received().get().clear();
|
S.Received().get().clear();
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
Util::sleep(10); //sleep 10ms if no data
|
Util::sleep(500); //sleep 500ms if no data
|
||||||
}
|
}
|
||||||
unsigned int now = Util::epoch();
|
unsigned int now = Util::epoch();
|
||||||
if (now != lastStats){
|
if (now != lastStats){
|
||||||
|
|
|
@ -568,12 +568,12 @@ namespace Connector_RTMP {
|
||||||
ss.setBlocking(false);
|
ss.setBlocking(false);
|
||||||
Strm.waitForMeta(ss);
|
Strm.waitForMeta(ss);
|
||||||
//find first audio and video tracks
|
//find first audio and video tracks
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (videoID == -1 && objIt->second["type"].asStringRef() == "video"){
|
if (videoID == -1 && it->second.type == "video"){
|
||||||
videoID = objIt->second["trackid"].asInt();
|
videoID = it->second.trackID;
|
||||||
}
|
}
|
||||||
if (audioID == -1 && objIt->second["type"].asStringRef() == "audio"){
|
if (audioID == -1 && it->second.type == "audio"){
|
||||||
audioID = objIt->second["trackid"].asInt();
|
audioID = it->second.trackID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//select the tracks and play
|
//select the tracks and play
|
||||||
|
@ -612,7 +612,7 @@ namespace Connector_RTMP {
|
||||||
amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
|
amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
|
||||||
sendCommand(amfreply, playMessageType, playStreamId);
|
sendCommand(amfreply, playMessageType, playStreamId);
|
||||||
//send streamisrecorded if stream, well, is recorded.
|
//send streamisrecorded if stream, well, is recorded.
|
||||||
if (Strm.metadata.isMember("length") && Strm.metadata["length"].asInt() > 0){
|
if (Strm.metadata.vod){//isMember("length") && Strm.metadata["length"].asInt() > 0){
|
||||||
Socket.Send(RTMPStream::SendUSR(4, 1)); //send UCM StreamIsRecorded (4), stream 1
|
Socket.Send(RTMPStream::SendUSR(4, 1)); //send UCM StreamIsRecorded (4), stream 1
|
||||||
}
|
}
|
||||||
//send streambegin
|
//send streambegin
|
||||||
|
@ -638,14 +638,14 @@ namespace Connector_RTMP {
|
||||||
|
|
||||||
//sent init data if needed
|
//sent init data if needed
|
||||||
if ( !streamInited){
|
if ( !streamInited){
|
||||||
init_tag.DTSCMetaInit(Strm, Strm.getTrackById(videoID), Strm.getTrackById(audioID));
|
init_tag.DTSCMetaInit(Strm, Strm.metadata.tracks[videoID], Strm.metadata.tracks[audioID]);
|
||||||
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
||||||
if (audioID != -1 && Strm.getTrackById(audioID).isMember("init")){
|
if (audioID != -1){
|
||||||
init_tag.DTSCAudioInit(Strm.getTrackById(audioID));
|
init_tag.DTSCAudioInit(Strm.metadata.tracks[audioID]);
|
||||||
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
||||||
}
|
}
|
||||||
if (videoID != -1 && Strm.getTrackById(videoID).isMember("init")){
|
if (videoID != -1){
|
||||||
init_tag.DTSCVideoInit(Strm.getTrackById(videoID));
|
init_tag.DTSCVideoInit(Strm.metadata.tracks[videoID]);
|
||||||
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
||||||
}
|
}
|
||||||
streamInited = true;
|
streamInited = true;
|
||||||
|
|
|
@ -61,36 +61,25 @@ namespace Connector_TS {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(trackIDs == ""){
|
if(trackIDs == ""){
|
||||||
|
std::stringstream tmpTracks;
|
||||||
// no track ids given? Find the first video and first audio track (if available) and use those!
|
// no track ids given? Find the first video and first audio track (if available) and use those!
|
||||||
int videoID = -1;
|
int videoID = -1;
|
||||||
int audioID = -1;
|
int audioID = -1;
|
||||||
|
|
||||||
Strm.waitForMeta(ss);
|
Strm.waitForMeta(ss);
|
||||||
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it){
|
||||||
|
if (audioID == -1 && it->second.type == "audio"){
|
||||||
|
audioID = it->first;
|
||||||
|
tmpTracks << " " << it->first;
|
||||||
|
}
|
||||||
|
|
||||||
if (Strm.metadata.isMember("tracks")){
|
if (videoID == -1 && it->second.type == "video"){
|
||||||
|
videoID = it->first;
|
||||||
|
tmpTracks << " " << it->first;
|
||||||
|
}
|
||||||
|
|
||||||
for (JSON::ObjIter trackIt = Strm.metadata["tracks"].ObjBegin(); trackIt != Strm.metadata["tracks"].ObjEnd(); trackIt++){
|
} // for iterator
|
||||||
|
trackIDs += tmpTracks.str();
|
||||||
if (audioID == -1 && trackIt->second["type"].asString() == "audio"){
|
|
||||||
audioID = trackIt->second["trackid"].asInt();
|
|
||||||
if( trackIDs != ""){
|
|
||||||
trackIDs += " " + trackIt->second["trackid"].asString();
|
|
||||||
}else{
|
|
||||||
trackIDs = trackIt->second["trackid"].asString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoID == -1 && trackIt->second["type"].asString() == "video"){
|
|
||||||
videoID = trackIt->second["trackid"].asInt();
|
|
||||||
if( trackIDs != ""){
|
|
||||||
trackIDs += " " + trackIt->second["trackid"].asString();
|
|
||||||
}else{
|
|
||||||
trackIDs = trackIt->second["trackid"].asString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // for iterator
|
|
||||||
} // if isMember("tracks")
|
|
||||||
} // if trackIDs == ""
|
} // if trackIDs == ""
|
||||||
|
|
||||||
std::string cmd = "t " + trackIDs + "\ns 0\np\n";
|
std::string cmd = "t " + trackIDs + "\ns 0\np\n";
|
||||||
|
@ -115,7 +104,7 @@ namespace Connector_TS {
|
||||||
char * ContCounter = 0;
|
char * ContCounter = 0;
|
||||||
if (Strm.lastType() == DTSC::VIDEO){
|
if (Strm.lastType() == DTSC::VIDEO){
|
||||||
if ( !haveAvcc){
|
if ( !haveAvcc){
|
||||||
avccbox.setPayload(Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["init"].asString());
|
avccbox.setPayload(Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].init);
|
||||||
haveAvcc = true;
|
haveAvcc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +128,7 @@ namespace Connector_TS {
|
||||||
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
||||||
ContCounter = &VideoCounter;
|
ContCounter = &VideoCounter;
|
||||||
}else if (Strm.lastType() == DTSC::AUDIO){
|
}else if (Strm.lastType() == DTSC::AUDIO){
|
||||||
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["init"].asString()));
|
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].init));
|
||||||
ToPack.append(Strm.lastData());
|
ToPack.append(Strm.lastData());
|
||||||
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
|
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
|
||||||
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
||||||
|
|
|
@ -153,8 +153,8 @@ namespace Controller {
|
||||||
float onemin, fivemin, fifteenmin;
|
float onemin, fivemin, fifteenmin;
|
||||||
if (sscanf(line, "%f %f %f", &onemin, &fivemin, &fifteenmin) == 3){
|
if (sscanf(line, "%f %f %f", &onemin, &fivemin, &fifteenmin) == 3){
|
||||||
capa["load"]["one"] = (long long int)(onemin * 100);
|
capa["load"]["one"] = (long long int)(onemin * 100);
|
||||||
capa["load"]["five"] = (long long int)(onemin * 100);
|
capa["load"]["five"] = (long long int)(fivemin * 100);
|
||||||
capa["load"]["fifteen"] = (long long int)(onemin * 100);
|
capa["load"]["fifteen"] = (long long int)(fifteenmin * 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,25 +33,25 @@ namespace Converters {
|
||||||
if (Strm.parsePacket(inBuffer)){
|
if (Strm.parsePacket(inBuffer)){
|
||||||
if ( !doneheader){
|
if ( !doneheader){
|
||||||
//find first audio and video tracks
|
//find first audio and video tracks
|
||||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
if (videoID == -1 && objIt->second["type"].asString() == "video"){
|
if (videoID == -1 && it->second.type == "video"){
|
||||||
videoID = objIt->second["trackid"].asInt();
|
videoID = it->first;
|
||||||
}
|
}
|
||||||
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
|
if (audioID == -1 && it->second.type == "audio"){
|
||||||
audioID = objIt->second["trackid"].asInt();
|
audioID = it->first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doneheader = true;
|
doneheader = true;
|
||||||
std::cout.write(FLV::Header, 13);
|
std::cout.write(FLV::Header, 13);
|
||||||
FLV_out.DTSCMetaInit(Strm, Strm.getTrackById(videoID), Strm.getTrackById(audioID));
|
FLV_out.DTSCMetaInit(Strm, Strm.metadata.tracks[videoID], Strm.metadata.tracks[audioID]);
|
||||||
std::cout.write(FLV_out.data, FLV_out.len);
|
std::cout.write(FLV_out.data, FLV_out.len);
|
||||||
if (videoID && Strm.getTrackById(videoID).isMember("init")){
|
if (videoID != -1){
|
||||||
FLV_out.DTSCVideoInit(Strm.getTrackById(videoID));
|
FLV_out.DTSCVideoInit(Strm.metadata.tracks[videoID]);
|
||||||
std::cout.write(FLV_out.data, FLV_out.len);
|
std::cout.write(FLV_out.data, FLV_out.len);
|
||||||
}
|
}
|
||||||
if (audioID && Strm.getTrackById(audioID).isMember("init")){
|
if (audioID != -1){
|
||||||
FLV_out.DTSCAudioInit(Strm.getTrackById(audioID));
|
FLV_out.DTSCAudioInit(Strm.metadata.tracks[audioID]);
|
||||||
std::cout.write(FLV_out.data, FLV_out.len);
|
std::cout.write(FLV_out.data, FLV_out.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,12 @@ namespace Converters {
|
||||||
int DTSC2MP4(Util::Config & conf){
|
int DTSC2MP4(Util::Config & conf){
|
||||||
DTSC::File input(conf.getString("filename"));//DTSC input
|
DTSC::File input(conf.getString("filename"));//DTSC input
|
||||||
MP4::DTSC2MP4Converter Conv;//DTSC to MP4 converter class will handle header creation and media parsing
|
MP4::DTSC2MP4Converter Conv;//DTSC to MP4 converter class will handle header creation and media parsing
|
||||||
std::cout << Conv.DTSCMeta2MP4Header(input.getMeta());//Creating and outputting MP4 header from DTSC file
|
std::cout << Conv.DTSCMeta2MP4Header(input.getMeta().toJSON());//Creating and outputting MP4 header from DTSC file
|
||||||
|
|
||||||
//initialising JSON input
|
//initialising JSON input
|
||||||
std::set<int> selector;
|
std::set<int> selector;
|
||||||
for (JSON::ObjIter trackIt = input.getMeta()["tracks"].ObjBegin(); trackIt != input.getMeta()["tracks"].ObjEnd(); trackIt++){
|
JSON::Value tmp = input.getMeta().toJSON();
|
||||||
|
for (JSON::ObjIter trackIt = tmp["tracks"].ObjBegin(); trackIt != tmp["tracks"].ObjEnd(); trackIt++){
|
||||||
selector.insert(trackIt->second["trackid"].asInt());
|
selector.insert(trackIt->second["trackid"].asInt());
|
||||||
}
|
}
|
||||||
input.selectTracks(selector);
|
input.selectTracks(selector);
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace Converters{
|
||||||
char* curNewPayload;
|
char* curNewPayload;
|
||||||
OGG::headerPages oggMeta;
|
OGG::headerPages oggMeta;
|
||||||
//Creating ID headers for theora and vorbis
|
//Creating ID headers for theora and vorbis
|
||||||
oggMeta.readDTSCHeader(DTSCFile.getMeta());
|
oggMeta.readDTSCHeader(DTSCFile.getMeta().toJSON());
|
||||||
std::cout << oggMeta.parsedPages;//outputting header pages
|
std::cout << oggMeta.parsedPages;//outputting header pages
|
||||||
|
|
||||||
//create DTSC in OGG pages
|
//create DTSC in OGG pages
|
||||||
|
|
|
@ -32,10 +32,22 @@ namespace Converters {
|
||||||
MP4::AVCC avccbox;
|
MP4::AVCC avccbox;
|
||||||
bool haveAvcc = false;
|
bool haveAvcc = false;
|
||||||
std::stringstream TSBuf;
|
std::stringstream TSBuf;
|
||||||
|
int videoID = -1;
|
||||||
|
int audioID = -1;
|
||||||
|
|
||||||
|
|
||||||
while (std::cin.good()){
|
while (std::cin.good()){
|
||||||
if (Strm.parsePacket(StrData)){
|
if (Strm.parsePacket(StrData)){
|
||||||
|
if ((videoID == -1 || audioID == -1) && Strm.metadata){
|
||||||
|
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||||
|
if (videoID == -1 && it->second.type == "video"){
|
||||||
|
videoID = it->first;
|
||||||
|
}
|
||||||
|
if (audioID == -1 && it->second.type == "audio"){
|
||||||
|
audioID = it->first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Strm.lastType() == DTSC::PAUSEMARK){
|
if (Strm.lastType() == DTSC::PAUSEMARK){
|
||||||
TSBuf.flush();
|
TSBuf.flush();
|
||||||
if (TSBuf.str().size()){
|
if (TSBuf.str().size()){
|
||||||
|
@ -46,7 +58,7 @@ namespace Converters {
|
||||||
TSBuf.str("");
|
TSBuf.str("");
|
||||||
}
|
}
|
||||||
if ( !haveAvcc){
|
if ( !haveAvcc){
|
||||||
avccbox.setPayload(Strm.metadata["video"]["init"].asString());
|
avccbox.setPayload(Strm.metadata.tracks[videoID].init);
|
||||||
haveAvcc = true;
|
haveAvcc = true;
|
||||||
}
|
}
|
||||||
if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
|
if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
|
||||||
|
@ -83,7 +95,7 @@ namespace Converters {
|
||||||
PIDno = 0x100;
|
PIDno = 0x100;
|
||||||
ContCounter = &VideoCounter;
|
ContCounter = &VideoCounter;
|
||||||
}else if (Strm.lastType() == DTSC::AUDIO){
|
}else if (Strm.lastType() == DTSC::AUDIO){
|
||||||
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata["audio"]["init"].asString()));
|
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata.tracks[audioID].init));
|
||||||
ToPack.append(Strm.lastData());
|
ToPack.append(Strm.lastData());
|
||||||
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
|
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
|
||||||
PIDno = 0x101;
|
PIDno = 0x101;
|
||||||
|
|
|
@ -8,19 +8,6 @@
|
||||||
|
|
||||||
///\brief Holds everything unique to converters.
|
///\brief Holds everything unique to converters.
|
||||||
namespace Converters {
|
namespace Converters {
|
||||||
class HeaderEntryDTSC {
|
|
||||||
public:
|
|
||||||
HeaderEntryDTSC() : totalSize(0), lastKeyTime(-5001), trackID(0), firstms(0x7FFFFFFF), lastms(0), keynum(0) {}
|
|
||||||
long long int totalSize;
|
|
||||||
std::vector<long long int> parts;
|
|
||||||
long long int lastKeyTime;
|
|
||||||
long long int trackID;
|
|
||||||
long long int firstms;
|
|
||||||
long long int lastms;
|
|
||||||
long long int keynum;
|
|
||||||
std::string type;
|
|
||||||
};
|
|
||||||
|
|
||||||
///\brief Reads a DTSC file and attempts to fix the metadata in it.
|
///\brief Reads a DTSC file and attempts to fix the metadata in it.
|
||||||
///\param conf The current configuration of the program.
|
///\param conf The current configuration of the program.
|
||||||
///\return The return code for the fixed program.
|
///\return The return code for the fixed program.
|
||||||
|
@ -29,232 +16,39 @@ namespace Converters {
|
||||||
F.seek_bpos(0);
|
F.seek_bpos(0);
|
||||||
F.parseNext();
|
F.parseNext();
|
||||||
JSON::Value oriheader = F.getJSON();
|
JSON::Value oriheader = F.getJSON();
|
||||||
JSON::Value meta = F.getMeta();
|
DTSC::Meta meta(F.getMeta());
|
||||||
JSON::Value pack;
|
|
||||||
|
|
||||||
if ( !oriheader.isMember("moreheader")){
|
if (meta.isFixed() && !conf.getBool("force")){
|
||||||
std::cerr << "This file is too old to fix - please reconvert." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (DTSC::isFixed(meta) && !conf.getBool("force")){
|
|
||||||
std::cerr << "This file was already fixed or doesn't need fixing - cancelling." << std::endl;
|
std::cerr << "This file was already fixed or doesn't need fixing - cancelling." << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
meta.removeMember("isFixed");
|
|
||||||
meta.removeMember("keytime");
|
|
||||||
meta.removeMember("keybpos");
|
|
||||||
meta.removeMember("moreheader");
|
|
||||||
|
|
||||||
std::map<std::string,int> trackIDs;
|
|
||||||
std::map<std::string,HeaderEntryDTSC> trackData;
|
|
||||||
|
|
||||||
|
|
||||||
long long int nowpack = 0;
|
|
||||||
|
|
||||||
std::string currentID;
|
|
||||||
int nextFreeID = 0;
|
|
||||||
|
|
||||||
for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++){
|
|
||||||
trackIDs.insert(std::pair<std::string,int>(it->first,it->second["trackid"].asInt()));
|
|
||||||
trackData[it->first].type = it->second["type"].asString();
|
|
||||||
trackData[it->first].trackID = it->second["trackid"].asInt();
|
|
||||||
trackData[it->first].type = it->second["type"].asString();
|
|
||||||
if (it->second["trackid"].asInt() >= nextFreeID){
|
|
||||||
nextFreeID = it->second["trackid"].asInt() + 1;
|
|
||||||
}
|
|
||||||
it->second.removeMember("keylen");
|
|
||||||
it->second.removeMember("keybpos");
|
|
||||||
it->second.removeMember("frags");
|
|
||||||
it->second.removeMember("keytime");
|
|
||||||
it->second.removeMember("keynum");
|
|
||||||
it->second.removeMember("keydata");
|
|
||||||
it->second.removeMember("keyparts");
|
|
||||||
it->second.removeMember("keys");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
meta.reset();
|
||||||
|
int bPos = F.getBytePos();
|
||||||
F.parseNext();
|
F.parseNext();
|
||||||
while ( !F.getJSON().isNull()){
|
while ( !F.getJSON().isNull()){
|
||||||
currentID = "";
|
F.getJSON()["bpos"] = bPos;
|
||||||
if (F.getJSON()["trackid"].asInt() == 0){
|
meta.update(F.getJSON());
|
||||||
if (F.getJSON()["datatype"].asString() == "video"){
|
bPos = F.getBytePos();
|
||||||
currentID = "video0";
|
|
||||||
if (trackData[currentID].trackID == 0){
|
|
||||||
trackData[currentID].trackID = nextFreeID++;
|
|
||||||
}
|
|
||||||
if (meta.isMember("video")){
|
|
||||||
meta["tracks"][currentID] = meta["video"];
|
|
||||||
meta.removeMember("video");
|
|
||||||
}
|
|
||||||
trackData[currentID].type = F.getJSON()["datatype"].asString();
|
|
||||||
}else{
|
|
||||||
if (F.getJSON()["datatype"].asString() == "audio"){
|
|
||||||
currentID = "audio0";
|
|
||||||
if (trackData[currentID].trackID == 0){
|
|
||||||
trackData[currentID].trackID = nextFreeID++;
|
|
||||||
}
|
|
||||||
if (meta.isMember("audio")){
|
|
||||||
meta["tracks"][currentID] = meta["audio"];
|
|
||||||
meta.removeMember("audio");
|
|
||||||
}
|
|
||||||
trackData[currentID].type = F.getJSON()["datatype"].asString();
|
|
||||||
}else{
|
|
||||||
//fprintf(stderr, "Found an unknown package with packetid 0 and datatype %s\n",F.getJSON()["datatype"].asString().c_str());
|
|
||||||
F.parseNext();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
for( std::map<std::string,int>::iterator it = trackIDs.begin(); it != trackIDs.end(); it++ ) {
|
|
||||||
if( it->second == F.getJSON()["trackid"].asInt() ) {
|
|
||||||
currentID = it->first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( currentID == "" ) {
|
|
||||||
//fprintf(stderr, "Found an unknown v2 packet with id %d\n", F.getJSON()["trackid"].asInt());
|
|
||||||
F.parseNext();
|
|
||||||
continue;
|
|
||||||
//should create new track but this shouldnt be needed...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (F.getJSON()["time"].asInt() < trackData[currentID].firstms){
|
|
||||||
trackData[currentID].firstms = F.getJSON()["time"].asInt();
|
|
||||||
}
|
|
||||||
if (F.getJSON()["time"].asInt() >= nowpack){
|
|
||||||
nowpack = F.getJSON()["time"].asInt();
|
|
||||||
}
|
|
||||||
if (trackData[currentID].type == "video"){
|
|
||||||
if (F.getJSON().isMember("keyframe")){
|
|
||||||
int newNum = meta["tracks"][currentID]["keys"].size();
|
|
||||||
meta["tracks"][currentID]["keys"][newNum]["num"] = ++trackData[currentID].keynum;
|
|
||||||
meta["tracks"][currentID]["keys"][newNum]["time"] = F.getJSON()["time"];
|
|
||||||
meta["tracks"][currentID]["keys"][newNum]["bpos"] = F.getLastReadPos();
|
|
||||||
if (meta["tracks"][currentID]["keys"].size() > 1){
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["len"] = F.getJSON()["time"].asInt() - meta["tracks"][currentID]["keys"][newNum - 1]["time"].asInt();
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["size"] = trackData[currentID].totalSize;
|
|
||||||
trackData[currentID].totalSize = 0;
|
|
||||||
std::string encodeVec = JSON::encodeVector( trackData[currentID].parts.begin(), trackData[currentID].parts.end() );
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["parts"] = encodeVec;
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["partsize"] = (long long int)trackData[currentID].parts.size();
|
|
||||||
trackData[currentID].parts.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if ((F.getJSON()["time"].asInt() - trackData[currentID].lastKeyTime) > 1000){
|
|
||||||
trackData[currentID].lastKeyTime = F.getJSON()["time"].asInt();
|
|
||||||
int newNum = meta["tracks"][currentID]["keys"].size();
|
|
||||||
meta["tracks"][currentID]["keys"][newNum]["num"] = ++trackData[currentID].keynum;
|
|
||||||
meta["tracks"][currentID]["keys"][newNum]["time"] = F.getJSON()["time"];
|
|
||||||
meta["tracks"][currentID]["keys"][newNum]["bpos"] = F.getLastReadPos();
|
|
||||||
if (meta["tracks"][currentID]["keys"].size() > 1){
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["len"] = F.getJSON()["time"].asInt() - meta["tracks"][currentID]["keys"][newNum - 1]["time"].asInt();
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["size"] = trackData[currentID].totalSize;
|
|
||||||
trackData[currentID].totalSize = 0;
|
|
||||||
std::string encodeVec = JSON::encodeVector( trackData[currentID].parts.begin(), trackData[currentID].parts.end() );
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["parts"] = encodeVec;
|
|
||||||
meta["tracks"][currentID]["keys"][newNum - 1]["partsize"] = (long long int)trackData[currentID].parts.size();
|
|
||||||
trackData[currentID].parts.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trackData[currentID].totalSize += F.getJSON()["data"].asString().size();
|
|
||||||
trackData[currentID].lastms = nowpack;
|
|
||||||
trackData[currentID].parts.push_back(F.getJSON()["data"].asString().size());
|
|
||||||
F.parseNext();
|
F.parseNext();
|
||||||
}
|
}
|
||||||
|
for (std::map<int,DTSC::Track>::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it++){
|
||||||
long long int firstms = 0x7fffffff;
|
if (it->second.fragments.size()){
|
||||||
long long int lastms = -1;
|
it->second.fragments.rbegin()->setDuration(it->second.fragments.rbegin()->getDuration() - it->second.lastms);
|
||||||
|
|
||||||
for (std::map<std::string,HeaderEntryDTSC>::iterator it = trackData.begin(); it != trackData.end(); it++){
|
|
||||||
if (it->second.firstms < firstms){
|
|
||||||
firstms = it->second.firstms;
|
|
||||||
}
|
}
|
||||||
if (it->second.lastms > lastms){
|
if (it->second.lastms / 1000){
|
||||||
lastms = it->second.lastms;
|
it->second.bps /= (it->second.lastms / 1000);
|
||||||
}
|
}
|
||||||
meta["tracks"][it->first]["firstms"] = it->second.firstms;
|
|
||||||
meta["tracks"][it->first]["lastms"] = it->second.lastms;
|
|
||||||
meta["tracks"][it->first]["length"] = (it->second.lastms - it->second.firstms) / 1000;
|
|
||||||
if ( !meta["tracks"][it->first].isMember("bps")){
|
|
||||||
meta["tracks"][it->first]["bps"] = (long long int)(it->second.lastms / ((it->second.lastms - it->second.firstms) / 1000));
|
|
||||||
}
|
|
||||||
if (it->second.trackID != 0){
|
|
||||||
meta["tracks"][it->first]["trackid"] = trackIDs[it->first];
|
|
||||||
}else{
|
|
||||||
meta["tracks"][it->first]["trackid"] = nextFreeID ++;
|
|
||||||
}
|
|
||||||
meta["tracks"][it->first]["type"] = it->second.type;
|
|
||||||
int tmp = meta["tracks"][it->first]["keys"].size();
|
|
||||||
if (tmp > 0){
|
|
||||||
if (tmp > 1){
|
|
||||||
meta["tracks"][it->first]["keys"][tmp - 1]["len"] = it->second.lastms - meta["tracks"][it->first]["keys"][tmp - 2]["time"].asInt();
|
|
||||||
}else{
|
|
||||||
meta["tracks"][it->first]["keys"][tmp - 1]["len"] = it->second.lastms;
|
|
||||||
}
|
|
||||||
meta["tracks"][it->first]["keys"][tmp - 1]["size"] = it->second.totalSize;
|
|
||||||
std::string encodeVec = JSON::encodeVector( trackData[it->first].parts.begin(), trackData[it->first].parts.end() );
|
|
||||||
meta["tracks"][it->first]["keys"][tmp - 1]["parts"] = encodeVec;
|
|
||||||
meta["tracks"][it->first]["keys"][tmp - 1]["partsize"] = (long long int)trackData[it->first].parts.size();
|
|
||||||
}else{
|
|
||||||
meta["tracks"][it->first]["keys"][tmp]["len"] = it->second.lastms;
|
|
||||||
meta["tracks"][it->first]["keys"][tmp]["size"] = it->second.totalSize;
|
|
||||||
std::string encodeVec = JSON::encodeVector( trackData[it->first].parts.begin(), trackData[it->first].parts.end() );
|
|
||||||
meta["tracks"][it->first]["keys"][tmp]["parts"] = encodeVec;
|
|
||||||
meta["tracks"][it->first]["keys"][tmp]["partsize"] = (long long int)trackData[it->first].parts.size();
|
|
||||||
meta["tracks"][it->first]["keys"][tmp]["time"] = it->second.firstms;
|
|
||||||
}
|
|
||||||
//calculate fragments
|
|
||||||
meta["tracks"][it->first]["frags"].null();
|
|
||||||
long long int currFrag = -1;
|
|
||||||
long long int maxBps = 0;
|
|
||||||
for (JSON::ArrIter arrIt = meta["tracks"][it->first]["keys"].ArrBegin(); arrIt != meta["tracks"][it->first]["keys"].ArrEnd(); arrIt++) {
|
|
||||||
if ((*arrIt)["time"].asInt() / 10000 > currFrag){
|
|
||||||
currFrag = (*arrIt)["time"].asInt() / 10000;
|
|
||||||
long long int fragLen = 1;
|
|
||||||
long long int fragDur = (*arrIt)["len"].asInt();
|
|
||||||
long long int fragSize = (*arrIt)["size"].asInt();
|
|
||||||
for (JSON::ArrIter it2 = arrIt + 1; it2 != meta["tracks"][it->first]["keys"].ArrEnd(); it2++){
|
|
||||||
if ((*it2)["time"].asInt() / 10000 > currFrag || (it2 + 1) == meta["tracks"][it->first]["keys"].ArrEnd()){
|
|
||||||
JSON::Value thisFrag;
|
|
||||||
thisFrag["num"] = (*arrIt)["num"].asInt();
|
|
||||||
thisFrag["time"] = (*arrIt)["time"].asInt();
|
|
||||||
thisFrag["len"] = fragLen;
|
|
||||||
thisFrag["dur"] = fragDur;
|
|
||||||
thisFrag["size"] = fragSize;
|
|
||||||
if (fragDur / 1000){
|
|
||||||
thisFrag["bps"] = fragSize / (fragDur / 1000);
|
|
||||||
if (maxBps < (fragSize / (fragDur / 1000))){
|
|
||||||
maxBps = (fragSize / (fragDur / 999));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thisFrag["bps"] = 1;
|
|
||||||
}
|
|
||||||
meta["tracks"][it->first]["frags"].append(thisFrag);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fragLen ++;
|
|
||||||
fragDur += (*it2)["len"].asInt();
|
|
||||||
fragSize += (*it2)["size"].asInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
meta["tracks"][it->first]["maxbps"] = maxBps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meta["firstms"] = firstms;
|
|
||||||
meta["lastms"] = lastms;
|
|
||||||
meta["length"] = (lastms - firstms) / 1000;
|
|
||||||
|
|
||||||
//append the revised header
|
//append the revised header
|
||||||
std::string loader = meta.toPacked();
|
std::string loader = meta.toJSON().toPacked();
|
||||||
long long int newHPos = F.addHeader(loader);
|
oriheader["moreheader"] = F.addHeader(loader);
|
||||||
if ( !newHPos){
|
if ( !oriheader["moreheader"].asInt()){
|
||||||
std::cerr << "Failure appending new header." << std::endl;
|
std::cerr << "Failure appending new header." << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//re-write the original header with information about the location of the new one
|
//re-write the original header with information about the location of the new one
|
||||||
oriheader["moreheader"] = newHPos;
|
|
||||||
loader = oriheader.toPacked();
|
loader = oriheader.toPacked();
|
||||||
if (F.writeHeader(loader)){
|
if (F.writeHeader(loader)){
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -57,7 +57,7 @@ namespace Converters {
|
||||||
fullSort = false;
|
fullSort = false;
|
||||||
}else{
|
}else{
|
||||||
DTSC::File F(tmpFileName);
|
DTSC::File F(tmpFileName);
|
||||||
if (!DTSC::isFixed(F.getMeta())){
|
if (!F.getMeta().isFixed()){
|
||||||
std::cerr << tmpFileName << " has not been run through DTSCFix yet." << std::endl;
|
std::cerr << tmpFileName << " has not been run through DTSCFix yet." << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -69,11 +69,11 @@ namespace Converters {
|
||||||
outFile = DTSC::File(outFileName, true);
|
outFile = DTSC::File(outFileName, true);
|
||||||
}else{
|
}else{
|
||||||
outFile = DTSC::File(outFileName);
|
outFile = DTSC::File(outFileName);
|
||||||
if (!DTSC::isFixed(outFile.getMeta())){
|
if ( !outFile.getMeta().isFixed()){
|
||||||
std::cerr << outFileName << " has not been run through DTSCFix yet." << std::endl;
|
std::cerr << outFileName << " has not been run through DTSCFix yet." << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
meta = outFile.getMeta();
|
meta = outFile.getMeta().toJSON();
|
||||||
newMeta = meta;
|
newMeta = meta;
|
||||||
if (meta.isMember("tracks") && meta["tracks"].size() > 0){
|
if (meta.isMember("tracks") && meta["tracks"].size() > 0){
|
||||||
for (JSON::ObjIter trackIt = meta["tracks"].ObjBegin(); trackIt != meta["tracks"].ObjEnd(); trackIt++){
|
for (JSON::ObjIter trackIt = meta["tracks"].ObjBegin(); trackIt != meta["tracks"].ObjEnd(); trackIt++){
|
||||||
|
@ -86,7 +86,7 @@ namespace Converters {
|
||||||
std::multimap<int,keyframeInfo> allSorted;
|
std::multimap<int,keyframeInfo> allSorted;
|
||||||
|
|
||||||
for (std::map<std::string,DTSC::File>::iterator it = inFiles.begin(); it != inFiles.end(); it++){
|
for (std::map<std::string,DTSC::File>::iterator it = inFiles.begin(); it != inFiles.end(); it++){
|
||||||
JSON::Value tmpMeta = it->second.getMeta();
|
JSON::Value tmpMeta = it->second.getMeta().toJSON();
|
||||||
if (tmpMeta.isMember("tracks") && tmpMeta["tracks"].size() > 0){
|
if (tmpMeta.isMember("tracks") && tmpMeta["tracks"].size() > 0){
|
||||||
for (JSON::ObjIter trackIt = tmpMeta["tracks"].ObjBegin(); trackIt != tmpMeta["tracks"].ObjEnd(); trackIt++){
|
for (JSON::ObjIter trackIt = tmpMeta["tracks"].ObjBegin(); trackIt != tmpMeta["tracks"].ObjEnd(); trackIt++){
|
||||||
long long int oldID = trackIt->second["trackid"].asInt();
|
long long int oldID = trackIt->second["trackid"].asInt();
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Info {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DTSC::File F(argv[1]);
|
DTSC::File F(argv[1]);
|
||||||
JSON::Value fileSpecs = F.getMeta();
|
JSON::Value fileSpecs = F.getMeta().toJSON();
|
||||||
if( !fileSpecs ) {
|
if( !fileSpecs ) {
|
||||||
char ** cmd = (char**)malloc(3*sizeof(char*));
|
char ** cmd = (char**)malloc(3*sizeof(char*));
|
||||||
cmd[0] = (char*)"ffprobe";
|
cmd[0] = (char*)"ffprobe";
|
||||||
|
@ -95,8 +95,9 @@ namespace Info {
|
||||||
}
|
}
|
||||||
JSON::Value tracks = fileSpecs["tracks"];
|
JSON::Value tracks = fileSpecs["tracks"];
|
||||||
for(JSON::ObjIter trackIt = tracks.ObjBegin(); trackIt != tracks.ObjEnd(); trackIt++){
|
for(JSON::ObjIter trackIt = tracks.ObjBegin(); trackIt != tracks.ObjEnd(); trackIt++){
|
||||||
fileSpecs["tracks"][trackIt->first].removeMember("frags");
|
fileSpecs["tracks"][trackIt->first].removeMember("fragments");
|
||||||
fileSpecs["tracks"][trackIt->first].removeMember("keys");
|
fileSpecs["tracks"][trackIt->first].removeMember("keys");
|
||||||
|
fileSpecs["tracks"][trackIt->first].removeMember("parts");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf( "%s", fileSpecs.toString().c_str() );
|
printf( "%s", fileSpecs.toString().c_str() );
|
||||||
|
|
Loading…
Add table
Reference in a new issue