Metadata upgrades as well as improvements to the buffer in terms of debugging and code maintainability.

This commit is contained in:
Thulinma 2013-11-27 15:30:34 +01:00
parent bb1971e961
commit cde42ce36e
25 changed files with 390 additions and 664 deletions

View file

@ -17,108 +17,13 @@ namespace Analysers {
///\return The return code of the analyser.
int analyseDTSC(Util::Config conf){
DTSC::File F(conf.getString("filename"));
JSON::Value meta = F.getMeta();
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();
std::cout << F.getMeta().toJSON().toPrettyString() << std::endl;
F.parseNext();
while (F.getJSON()){
nowpack = F.getJSON()["time"].asInt();
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();
}
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;
}
}

View file

@ -27,6 +27,9 @@ namespace Buffer {
///\brief A function running in a thread to send all statistics.
///\param empty A null pointer.
void handleStats(void * empty){
#ifdef _TTHREAD_POSIX_
pthread_setname_np(pthread_self(), "StatsHandler");
#endif
if (empty != 0){
return;
}
@ -52,6 +55,9 @@ namespace Buffer {
///\brief A function to handle input data.
///\param conn A socket reference.
void handlePushIn(Socket::Connection & conn){
#ifdef _TTHREAD_POSIX_
pthread_setname_np(pthread_self(), "Push Input");
#endif
conn.setBlocking(true);
while (buffer_running && conn.connected()){
if (conn.spool()){
@ -70,6 +76,9 @@ namespace Buffer {
if (empty != 0){
return;
}
#ifdef _TTHREAD_POSIX_
pthread_setname_np(pthread_self(), "Standard Input");
#endif
long long int timeDiff = 0; //difference between local time and stream time
unsigned int lastPacket = 0; //last parsed packet timestamp
std::string inBuffer;
@ -105,7 +114,10 @@ namespace Buffer {
user * usr = (user*)v_usr;
thisStream->addUser(usr);
#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
usr->myRing = thisStream->getRing();
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()){
usr->myRing->playCount = 0;
JSON::Value pausemark;
pausemark["datatype"] = "pause_marker";
pausemark["trackid"] = 0ll;
pausemark["mark"] = "pause";
pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt();
pausemark.toPacked();
usr->S.SendNow(pausemark.toNetPacked());
pausemark.sendTo(usr->S);
}
}
}
@ -144,10 +156,10 @@ namespace Buffer {
if (usr->myRing->playCount < 1 || usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt()){
usr->myRing->playCount = 0;
JSON::Value pausemark;
pausemark["datatype"] = "pause_marker";
pausemark["trackid"] = 0ll;
pausemark["mark"] = "pause";
pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt();
pausemark.toPacked();
usr->S.SendNow(pausemark.toNetPacked());
pausemark.sendTo(usr->S);
}
}
}
@ -275,6 +287,9 @@ namespace Buffer {
}
SS.setBlocking(false);
conf.activate();
#ifdef _TTHREAD_POSIX_
pthread_setname_np(pthread_self(), "Main accepter");
#endif
thisStream = Stream::get();
thisStream->setName(name);
thisStream->setBufferTime(conf.getInteger("time"));
@ -299,7 +314,7 @@ namespace Buffer {
//starts a thread for every accepted connection
incoming = SS.accept(true);
if (incoming.connected()){
tthread::thread thisUser(handleUser, (void *)new user(incoming, userId++));
tthread::thread thisUser(handleUser, (void *)new user(incoming, ++userId));
thisUser.detach();
}else{
Util::sleep(50);//sleep 50ms

View file

@ -17,8 +17,7 @@ namespace Buffer {
creator.lock();
if (ref == 0){
ref = new Stream();
ref->metadata["tracks"] = 1ll;
ref->metadata["live"] = 1ll;
ref->metadata.live = true;
}
creator.unlock();
}
@ -194,7 +193,7 @@ namespace Buffer {
void Stream::sendMeta(Socket::Connection & s){
if (metadata){
rw_mutex.lock();
metadata.sendTo(s);
metadata.send(s);
rw_mutex.unlock();
}
}

View file

@ -11,7 +11,7 @@ namespace Buffer {
///Also prints "User connected" text to stdout.
///\param fd A connection to the user.
user::user(Socket::Connection fd, long long ID){
sID = JSON::Value(ID).asStringRef();
sID = JSON::Value(ID).asString();
S = fd;
curr_up = 0;
curr_down = 0;

View file

@ -81,15 +81,17 @@ int main(int argc, char** argv){
Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin));
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;
return 1;
}
source.getMeta().send(in_out);
JSON::Value pausemark;
pausemark["datatype"] = "pause_marker";
pausemark["trackid"] = 0ll;
pausemark["mark"] = "pause";
pausemark["time"] = (long long int)0;
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"]["start"] = Util::epoch() - sts.conntime;
if ( !meta_sent){
json_sts["vod"]["meta"] = source.getMeta();
json_sts["vod"]["meta"] = source.getMeta().toJSON();
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++){
oIt->second.removeMember("keys");
oIt->second.removeMember("frags");
oIt->second.removeMember("fragments");
oIt->second.removeMember("parts");
}
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;
#endif
pausemark["time"] = source.getJSON()["time"];
pausemark.netPrepare();
in_out.SendNow(pausemark.toNetPacked());
pausemark.sendTo(in_out);
in_out.setBlocking(true);
}else{
lasttime = Util::epoch();

View file

@ -27,16 +27,16 @@
/// Holds everything unique to HTTP Connectors.
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
void getTracks(JSON::Value & metadata){
void getTracks(DTSC::Meta & metadata){
videoTracks.clear();
for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){
if (it->second["type"] == "video"){
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
if (it->second.type == "video"){
videoTracks.insert(it->first);
}
if (it->second["type"] == "audio"){
audioTrack = it->second["trackid"].asInt();
if (it->second.type == "audio"){
audioTrack = it->first;
}
}
}
@ -47,7 +47,7 @@ namespace Connector_HTTP {
///\param metadata The current metadata, used to generate the index.
///\param fragnum The index of the current fragment
///\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;
MP4::ASRT asrt;
@ -57,7 +57,7 @@ namespace Connector_HTTP {
if (isLive){
asrt.setSegmentRun(1, 4294967295ul, 0);
}else{
asrt.setSegmentRun(1, metadata["keys"].size(), 0);
asrt.setSegmentRun(1, trackMeta.keys.size(), 0);
}
MP4::AFRT afrt;
@ -66,14 +66,14 @@ namespace Connector_HTTP {
afrt.setTimeScale(1000);
//afrt.setQualityEntry(empty, 0);
MP4::afrt_runtable afrtrun;
long long int currTime = 0;
for (int i = 0; i < metadata["keys"].size(); i++){
if (metadata["keys"][i]["len"].asInt() > 0){
afrtrun.firstFragment = metadata["keys"][i]["num"].asInt();
afrtrun.firstTimestamp = metadata["keys"][i]["time"].asInt();
afrtrun.duration = metadata["keys"][i]["len"].asInt();
currTime = afrtrun.firstTimestamp + afrtrun.duration;
int i = 0;
for (std::deque<DTSC::Key>::iterator it = trackMeta.keys.begin(); it != trackMeta.keys.end(); it++){
if (it->getLength()){
afrtrun.firstFragment = it->getNumber();
afrtrun.firstTimestamp = it->getTime();
afrtrun.duration = it->getLength();
afrt.setFragmentRun(afrtrun, i);
i++;
}
}
@ -84,7 +84,7 @@ namespace Connector_HTTP {
abst.setUpdate(false);
abst.setTimeScale(1000);
abst.setLive(isLive);
abst.setCurrentMediaTime(metadata["lastms"].asInt());
abst.setCurrentMediaTime(trackMeta.lastms);
abst.setSmpteTimeCodeOffset(0);
abst.setMovieIdentifier(streamName);
abst.setSegmentRunTable(asrt, 0);
@ -100,7 +100,7 @@ namespace Connector_HTTP {
///\param streamName The name of the stream.
///\param metadata The current metadata, used to generate the index.
///\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);}
std::stringstream Result;
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
@ -108,27 +108,27 @@ namespace Connector_HTTP {
Result << " <id>" << streamName << "</id>" << std::endl;
Result << " <mimeType>video/mp4</mimeType>" << std::endl;
Result << " <deliveryType>streaming</deliveryType>" << std::endl;
if (metadata.isMember("vod")){
Result << " <duration>" << metadata["length"].asInt() << ".000</duration>" << std::endl;
if (metadata.vod){
Result << " <duration>" << metadata.tracks[*videoTracks.begin()].lastms / 1000 << ".000</duration>" << std::endl;
Result << " <streamType>recorded</streamType>" << std::endl;
}else{
Result << " <duration>0.00</duration>" << 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 "
"profile=\"named\" "
"id=\"boot" << metadata["tracks"][(*it)]["trackid"].asInt() << "\" "
"url=\"" << metadata["tracks"][(*it)]["trackid"].asInt() << ".abst\">"
"id=\"boot" << (*it) << "\" "
"url=\"" << (*it) << ".abst\">"
"</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 "
"url=\"" << metadata["tracks"][(*it)]["trackid"].asInt() << "-\" "
"bitrate=\"" << metadata["tracks"][(*it)]["bps"].asInt() * 8 << "\" "
"bootstrapInfoId=\"boot" << metadata["tracks"][(*it)]["trackid"].asInt() << "\" "
"width=\"" << metadata["tracks"][(*it)]["width"].asInt() << "\" "
"height=\"" << metadata["tracks"][(*it)]["height"].asInt() << "\">" << std::endl;
"url=\"" << (*it) << "-\" "
"bitrate=\"" << metadata.tracks[(*it)].bps * 8 << "\" "
"bootstrapInfoId=\"boot" << (*it) << "\" "
"width=\"" << metadata.tracks[(*it)].width << "\" "
"height=\"" << metadata.tracks[(*it)].height << "\">" << std::endl;
Result << " <metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>" << std::endl;
Result << " </media>" << std::endl;
}
@ -183,7 +183,7 @@ namespace Connector_HTTP {
std::string streamID = HTTP_R.url.substr(streamname.size() + 10);
streamID = streamID.substr(0, streamID.find(".abst"));
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("Cache-Control", "no-cache");
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);
#endif
if (!audioTrack){getTracks(Strm.metadata);}
JSON::Value & vidTrack = Strm.getTrackById(Quality);
DTSC::Track & vidTrack = Strm.metadata.tracks[Quality];
mstime = 0;
mslen = 0;
if (vidTrack.isMember("keys")){
for (JSON::ArrIter it = vidTrack["keys"].ArrBegin(); it != vidTrack["keys"].ArrEnd(); it++){
if ((*it)["num"].asInt() >= ReqFragment){
mstime = (*it)["time"].asInt();
mslen = (*it)["len"].asInt();
if (Strm.metadata.isMember("live")){
if (it == vidTrack["keys"].ArrEnd() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
HTTP_S.SendResponse("208", "Ask again later", conn);
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
if (ss.spool()){
while (Strm.parsePacket(ss.Received())){}
}
for (std::deque<DTSC::Key>::iterator it = vidTrack.keys.begin(); it != vidTrack.keys.end(); it++){
if (it->getNumber() >= ReqFragment){
mstime = it->getTime();
mslen = it->getLength();
if (Strm.metadata.live){
if (it == vidTrack.keys.end() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
HTTP_S.SendResponse("208", "Ask again later", conn);
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
if (ss.spool()){
while (Strm.parsePacket(ss.Received())){}
}
}
break;
}
break;
}
}
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){
HTTP_S.Clean();
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.StartResponse(HTTP_R, conn);
//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);
//send a zero-size mdat, meaning it stretches until end of file.
HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn);
//send init data, if needed.
if (audioTrack > 0 && Strm.getTrackById(audioTrack).isMember("init")){
tmp.DTSCAudioInit(Strm.getTrackById(audioTrack));
if (audioTrack > 0){
tmp.DTSCAudioInit(Strm.metadata.tracks[audioTrack]);
tmp.tagTime(mstime);
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
}
if (Quality > 0 && Strm.getTrackById(Quality).isMember("init")){
tmp.DTSCVideoInit(Strm.getTrackById(Quality));
if (Quality > 0){
tmp.DTSCVideoInit(Strm.metadata.tracks[Quality]);
tmp.tagTime(mstime);
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
}

View file

@ -121,12 +121,14 @@ namespace Connector_HTTP {
std::stringstream cmd;
cmd << "t";
if (Strm.metadata["tracks"].size()){
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
if ( objIt->second["type"].asStringRef() == "meta" ){
cmd << " " << objIt->second["trackid"].asInt();
int tid = -1;
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
if (it->second.type == "meta" ){
if (tid == -1){
tid = it->second.trackID;
}
}
cmd << " " << it->second.trackID;
}
}
if( cmd.str() == "t" ){
@ -134,7 +136,7 @@ namespace Connector_HTTP {
cmd.clear();
}
int maxTime = Strm.metadata["lastms"].asInt();
int maxTime = Strm.metadata.tracks[tid].lastms;
cmd << "\ns " << seek_sec << "\np " << maxTime << "\n";
ss.SendNow(cmd.str().c_str(), cmd.str().size());

View file

@ -28,62 +28,65 @@ namespace Connector_HTTP {
///\brief Builds an index file for HTTP Live streaming.
///\param metadata The current metadata, used to generate the index.
///\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;
if (metadata.isMember("tracks")){
result << "#EXTM3U\r\n";
int audioId = -1;
std::string audioName;
bool defAudio = false;//set default audio track;
for (JSON::ObjIter trackIt = metadata["tracks"].ObjBegin(); trackIt != metadata["tracks"].ObjEnd(); trackIt++){
if (trackIt->second["type"].asStringRef() == "audio"){
audioId = trackIt->second["trackid"].asInt();
audioName = trackIt->first;
break;
}
result << "#EXTM3U\r\n";
int audioId = -1;
std::string audioName;
bool defAudio = false;//set default audio track;
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
if (it->second.type == "audio"){
audioId = it->first;
audioName = it->second.getIdentifier();
break;
}
for (JSON::ObjIter trackIt = metadata["tracks"].ObjBegin(); trackIt != metadata["tracks"].ObjEnd(); trackIt++){
if (trackIt->second["type"].asStringRef() == "video"){
int bWidth = trackIt->second["maxbps"].asInt();
if (audioId != -1){
bWidth += (metadata["tracks"][audioName]["maxbps"].asInt() * 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";
}
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
if (it->second.type == "video"){
int bWidth = it->second.bps * 2;
if (audioId != -1){
bWidth += metadata.tracks[audioId].bps * 2;
}
}
}else{
//parse single track
int longestFragment = 0;
for (JSON::ArrIter ai = metadata["frags"].ArrBegin(); ai != metadata["frags"].ArrEnd(); ai++){
if ((*ai)["dur"].asInt() > longestFragment){
longestFragment = (*ai)["dur"].asInt();
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";
}
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"
<< starttime << "_" << (*ai)["dur"].asInt() + starttime << ".ts\r\n";
}
if ( !isLive){
result << "#EXT-X-ENDLIST\r\n";
}
#if DEBUG >= 8
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
#endif
return result.str();
}
std::string liveIndex(DTSC::Track & metadata, bool isLive){
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();
}
}
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
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
#endif
@ -161,7 +164,7 @@ namespace Connector_HTTP {
lastVid = Segment * 90;
temp = HTTP_R.url.find("_", temp) + 1;
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);
if (seekable < 0){
HTTP_S.Clean();
@ -207,10 +210,10 @@ namespace Connector_HTTP {
HTTP_S.SetHeader("Cache-Control", "no-cache");
std::string manifest;
if (request.find("/") == std::string::npos){
manifest = liveIndex(Strm.metadata, Strm.metadata.isMember("live"));
manifest = liveIndex(Strm.metadata, Strm.metadata.live);
}else{
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);
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
@ -235,7 +238,7 @@ namespace Connector_HTTP {
handlingRequest = false;
}
if ( !haveAvcc){
avccbox.setPayload(Strm.getTrackById(trackID)["init"].asString());
avccbox.setPayload(Strm.metadata.tracks[trackID].init);
haveAvcc = true;
}
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));
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
ContCounter = &VideoCounter;
}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());
if (AppleCompat){
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), lastVid));

View file

@ -101,19 +101,19 @@ namespace Connector_HTTP {
}
Strm.waitForMeta(ss);
int byterate = 0;
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
if (videoID == -1 && objIt->second["type"].asString() == "video"){
videoID = objIt->second["trackid"].asInt();
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->second.trackID;
}
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
audioID = objIt->second["trackid"].asInt();
if (audioID == -1 && it->second.type == "audio"){
audioID = it->second.trackID;
}
}
if (videoID != -1){
byterate += Strm.getTrackById(videoID)["bps"].asInt();
byterate += Strm.metadata.tracks[videoID].bps;
}
if (audioID != -1){
byterate += Strm.getTrackById(audioID)["bps"].asInt();
byterate += Strm.metadata.tracks[audioID].bps;
}
if ( !byterate){byterate = 1;}
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(FLV::Header, 13); //write FLV header
//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);
//write video init data, if needed
if (videoID != -1 && Strm.getTrackById(videoID).isMember("init")){
tag.DTSCVideoInit(Strm.getTrackById(videoID));
if (videoID != -1){
tag.DTSCVideoInit(Strm.metadata.tracks[videoID]);
conn.SendNow(tag.data, tag.len);
}
//write audio init data, if needed
if (audioID != -1 && Strm.getTrackById(audioID).isMember("init")){
tag.DTSCAudioInit(Strm.getTrackById(audioID));
if (audioID != -1){
tag.DTSCAudioInit(Strm.metadata.tracks[audioID]);
conn.SendNow(tag.data, tag.len);
}
progressive_has_sent_header = true;
@ -170,7 +170,7 @@ namespace Connector_HTTP {
conn.close();
}
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"){
tag.DTSCLoader(Strm);
conn.SendNow(tag.data, tag.len); //write the tag contents

View file

@ -96,13 +96,13 @@ namespace Connector_HTTP {
}
Strm.waitForMeta(ss);
int byterate = 0;
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
if (audioID == -1 && objIt->second["codec"].asString() == "MP3"){
audioID = objIt->second["trackid"].asInt();
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
if (audioID == -1 && it->second.codec == "MP3"){
audioID = it->second.trackID;
}
}
if (audioID != -1){
byterate += Strm.getTrackById(audioID)["bps"].asInt();
byterate += Strm.metadata.tracks[audioID].bps;
}
if ( !byterate){byterate = 1;}
if (seek_byte){

View file

@ -98,7 +98,7 @@ namespace Connector_HTTP {
HTTP_S.SetHeader("Content-Type", "video/MP4"); //Send the correct content-type for FLV files
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(Conv.DTSCMeta2MP4Header(Strm.metadata));//SENDING MP4HEADER
conn.SendNow(Conv.DTSCMeta2MP4Header(Strm.metadata.toJSON()));//SENDING MP4HEADER
keyPartIt = Conv.keyParts.begin();
{//using scope to have cmd not declared after action
std::stringstream cmd;
@ -127,19 +127,19 @@ namespace Connector_HTTP {
}
}
int byterate = 0;
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
if (videoID == -1 && objIt->second["type"].asString() == "video"){
videoID = objIt->second["trackid"].asInt();
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->second.trackID;
}
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
audioID = objIt->second["trackid"].asInt();
if (audioID == -1 && it->second.type == "audio"){
audioID = it->second.trackID;
}
}
if (videoID != -1){
byterate += Strm.getTrackById(videoID)["bps"].asInt();
byterate += Strm.metadata.tracks[videoID].bps;
}
if (audioID != -1){
byterate += Strm.getTrackById(audioID)["bps"].asInt();
byterate += Strm.metadata.tracks[audioID].bps;
}
if ( !byterate){byterate = 1;}
seek_sec = (seek_byte / byterate) * 1000;

View file

@ -112,19 +112,19 @@ namespace Connector_HTTP {
}
Strm.waitForMeta(ss);
int byterate = 0;
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
if (videoID == -1 && objIt->second["codec"].asString() == "theora"){
videoID = objIt->second["trackid"].asInt();
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
if (videoID == -1 && it->second.codec == "theora"){
videoID = it->second.trackID;
}
if (audioID == -1 && objIt->second["codec"].asString() == "vorbis"){
audioID = objIt->second["trackid"].asInt();
if (audioID == -1 && it->second.codec == "vorbis"){
audioID = it->second.trackID;
}
}
if (videoID != -1){
byterate += Strm.getTrackById(videoID)["bps"].asInt();
byterate += Strm.metadata.tracks[videoID].bps;
}
if (audioID != -1){
byterate += Strm.getTrackById(audioID)["bps"].asInt();
byterate += Strm.metadata.tracks[audioID].bps;
}
if ( !byterate){byterate = 1;}
if (seek_byte){
@ -163,7 +163,7 @@ namespace Connector_HTTP {
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
//Fill in ogg header here
oggMeta.readDTSCHeader(Strm.metadata);
oggMeta.readDTSCHeader(Strm.metadata.toJSON());
conn.SendNow((char*)oggMeta.parsedPages.c_str(), oggMeta.parsedPages.size());
progressive_has_sent_header = true;
//setting sendReady to not ready

View file

@ -30,42 +30,42 @@ namespace Connector_HTTP {
///\brief Builds an index file for HTTP Smooth streaming.
///\param metadata The current metadata, used to generate the index.
///\return The index file for HTTP Smooth Streaming.
std::string smoothIndex(JSON::Value & metadata){
std::string smoothIndex(DTSC::Meta & metadata){
std::stringstream Result;
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
Result << "<SmoothStreamingMedia "
"MajorVersion=\"2\" "
"MinorVersion=\"0\" "
"TimeScale=\"10000000\" ";
if (metadata.isMember("vod")){
Result << "Duration=\"" << metadata["lastms"].asInt() << "0000\"";
}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;
std::map<int,DTSC::Track> allAudio;
std::map<int,DTSC::Track> allVideo;
long long int maxWidth = 0;
long long int maxHeight = 0;
long long int minWidth = 99999999;
long long int minHeight = 99999999;
for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){
if (oIt->second["type"].asString() == "audio" && oIt->second["codec"].asString() == "AAC"){
allAudio[oIt->first] = oIt->second;
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
if (it->second.type == "audio" && it->second.codec == "AAC"){
allAudio.insert(*it);
}
if (oIt->second["type"].asString() == "video" && oIt->second["codec"].asString() == "H264"){
allVideo[oIt->first] = oIt->second;
if (oIt->second["width"].asInt() > maxWidth){maxWidth = oIt->second["width"].asInt();}
if (oIt->second["width"].asInt() < minWidth){minWidth = oIt->second["width"].asInt();}
if (oIt->second["height"].asInt() > maxHeight){maxHeight = oIt->second["height"].asInt();}
if (oIt->second["height"].asInt() < minHeight){minHeight = oIt->second["height"].asInt();}
if (it->second.type == "video" && it->second.codec == "H264"){
allVideo.insert(*it);
if (it->second.width > maxWidth){maxWidth = it->second.width;}
if (it->second.width < minWidth){minWidth = it->second.width;}
if (it->second.height > maxHeight){maxHeight = it->second.height;}
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
if (allAudio.size()){
@ -73,36 +73,36 @@ namespace Connector_HTTP {
"Type=\"audio\" "
"QualityLevels=\"" << allAudio.size() << "\" "
"Name=\"audio\" "
"Chunks=\"" << allAudio.ObjBegin()->second["keys"].size() << "\" "
"Chunks=\"" << allAudio.begin()->second.keys.size() << "\" "
"Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n";
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 "
"Index=\"" << index << "\" "
"Bitrate=\"" << oIt->second["bps"].asInt() * 8 << "\" "
"Bitrate=\"" << it->second.bps * 8 << "\" "
"CodecPrivateData=\"" << std::hex;
for (int i = 0; i < oIt->second["init"].asString().size(); i++){
Result << std::setfill('0') << std::setw(2) << std::right << (int)oIt->second["init"].asString()[i];
for (int i = 0; i < it->second.init.size(); i++){
Result << std::setfill('0') << std::setw(2) << std::right << (int)it->second.init[i];
}
Result << std::dec << "\" "
"SamplingRate=\"" << oIt->second["rate"].asInt() << "\" "
"SamplingRate=\"" << it->second.rate << "\" "
"Channels=\"2\" "
"BitsPerSample=\"16\" "
"PacketSize=\"4\" "
"AudioTag=\"255\" "
"FourCC=\"AACL\" >\n";
Result << "<CustomAttributes>\n"
"<Attribute Name = \"TrackID\" Value = \"" << oIt->second["trackid"].asString() << "\" />"
"<Attribute Name = \"TrackID\" Value = \"" << it->first << "\" />"
"</CustomAttributes>";
Result << "</QualityLevel>\n";
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 ";
if (keyIt == allAudio.ObjBegin()->second["keys"].ArrBegin()){
Result << "t=\"" << allAudio.ObjBegin()->second["firstms"].asInt() * 10000 << "\" ";
if (it == allAudio.begin()->second.keys.begin()){
Result << "t=\"" << it->getTime() * 10000 << "\" ";
}
Result << "d=\"" << (*keyIt)["len"].asInt() * 10000 << "\" />\n";
Result << "d=\"" << it->getLength() * 10000 << "\" />\n";
}
Result << "</StreamIndex>\n";
}
@ -112,41 +112,41 @@ namespace Connector_HTTP {
"Type=\"video\" "
"QualityLevels=\"" << allVideo.size() << "\" "
"Name=\"video\" "
"Chunks=\"" << allVideo.ObjBegin()->second["keys"].size() << "\" "
"Chunks=\"" << allVideo.begin()->second.keys.size() << "\" "
"Url=\"Q({bitrate},{CustomAttributes})/V({start time})\" "
"MaxWidth=\"" << maxWidth << "\" "
"MaxHeight=\"" << maxHeight << "\" "
"DisplayWidth=\"" << maxWidth << "\" "
"DisplayHeight=\"" << maxHeight << "\">\n";
int index = 0;
for (JSON::ObjIter oIt = allVideo.ObjBegin(); oIt != allVideo.ObjEnd(); oIt++){
//Add video qualities
for (std::map<int,DTSC::Track>::iterator it = allVideo.begin(); it != allVideo.end(); it++){
//Add video qualities
Result << "<QualityLevel "
"Index=\"" << index << "\" "
"Bitrate=\"" << oIt->second["bps"].asInt() * 8 << "\" "
"Bitrate=\"" << it->second.bps * 8 << "\" "
"CodecPrivateData=\"" << std::hex;
MP4::AVCC avccbox;
avccbox.setPayload(oIt->second["init"].asString());
avccbox.setPayload(it->second.init);
std::string tmpString = avccbox.asAnnexB();
for (int i = 0; i < tmpString.size(); i++){
Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i];
}
Result << std::dec << "\" "
"MaxWidth=\"" << oIt->second["width"].asInt() << "\" "
"MaxHeight=\"" << oIt->second["height"].asInt() << "\" "
"MaxWidth=\"" << it->second.width << "\" "
"MaxHeight=\"" << it->second.height << "\" "
"FourCC=\"AVC1\" >\n";
Result << "<CustomAttributes>\n"
"<Attribute Name = \"TrackID\" Value = \"" << oIt->second["trackid"].asString() << "\" />"
"<Attribute Name = \"TrackID\" Value = \"" << it->first << "\" />"
"</CustomAttributes>";
Result << "</QualityLevel>\n";
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 ";
if (keyIt == allVideo.ObjBegin()->second["keys"].ArrBegin()){
Result << "t=\"" << (*keyIt)["time"].asInt() * 10000 << "\" ";
if (it == allVideo.begin()->second.keys.begin()){
Result << "t=\"" << it->getTime() * 10000 << "\" ";
}
Result << "d=\"" << (*keyIt)["len"].asInt() * 10000 << "\" />\n";
Result << "d=\"" << it->getLength() * 10000 << "\" />\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.
conn.setBlocking(false);//Set the client socket to non-blocking
JSON::Value allAudio;
JSON::Value allVideo;
std::map<int,DTSC::Track> allAudio;
std::map<int,DTSC::Track> allVideo;
while (conn.connected()){
if ( !handlingRequest){
@ -211,12 +211,12 @@ namespace Connector_HTTP {
}
ss.setBlocking(false);
Strm.waitForMeta(ss);
for (JSON::ObjIter oIt = Strm.metadata["tracks"].ObjBegin(); oIt != Strm.metadata["tracks"].ObjEnd(); oIt++){
if (oIt->second["type"].asString() == "audio" && oIt->second["codec"].asString() == "AAC"){
allAudio[oIt->first] = oIt->second;
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
if (it->second.type == "audio" && it->second.codec == "AAC"){
allAudio[it->first] = it->second;
}
if (oIt->second["type"].asString() == "video" && oIt->second["codec"].asString() == "H264"){
allVideo[oIt->first] = oIt->second;
if (it->second.type == "video" && it->second.codec == "H264"){
allVideo[it->first] = it->second;
}
}
};
@ -250,14 +250,14 @@ namespace Connector_HTTP {
parseString = parseString.substr(parseString.find("(") + 1);
requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str());
long long int selectedQuality = atoll(Quality.c_str());
JSON::Value & myRef = Strm.getTrackById(selectedQuality);
if (Strm.metadata.isMember("live")){
DTSC::Track & myRef = Strm.metadata.tracks[selectedQuality];
if (Strm.metadata.live){
int seekable = Strm.canSeekms(requestedTime / 10000);
if (seekable == 0){
// 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++){
if ((*aIt)["time"].asInt() >= (requestedTime / 10000)){
if ((aIt + 1) == myRef["keys"].ArrEnd()){
for (std::deque<DTSC::Key>::iterator it = myRef.keys.begin(); it != myRef.keys.end(); it++){
if (it->getTime() >= (requestedTime / 10000)){
if ((it + 1) == myRef.keys.end()){
seekable = 1;
}
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");
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
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;
}
if (seekable > 0){
@ -277,7 +277,7 @@ namespace Connector_HTTP {
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
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;
}
}
@ -286,26 +286,24 @@ namespace Connector_HTTP {
long long mstime = 0;
long long mslen = 0;
if (myRef.isMember("keys")){
for (JSON::ArrIter it = myRef["keys"].ArrBegin(); it != myRef["keys"].ArrEnd(); it++){
if ((*it)["time"].asInt() >= (requestedTime / 10000)){
mstime = (*it)["time"].asInt();
mslen = (*it)["len"].asInt();
if (Strm.metadata.isMember("live")){
if (it == myRef["keys"].ArrEnd() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl;
}
for (std::deque<DTSC::Key>::iterator it = myRef.keys.end(); it != myRef.keys.end(); it++){
if (it->getTime() >= (requestedTime / 10000)){
mstime = it->getTime();
mslen = it->getLength();
if (Strm.metadata.live){
if (it == myRef.keys.end() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
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 (Strm.metadata.isMember("live")){
if (Strm.metadata.live){
if (mstime == 0 && (requestedTime / 10000) > 1){
HTTP_S.Clean();
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;
JSON::Value trackRef;
DTSC::Track trackRef;
if (wantsVideo){
trackRef = allVideo.ObjBegin()->second;
trackRef = allVideo.begin()->second;
}
if (wantsAudio){
trackRef = allAudio.ObjBegin()->second;
trackRef = allAudio.begin()->second;
}
//Also obtain the associated keyframe;
JSON::Value keyObj;
for (JSON::ArrIter keyIt = trackRef["keys"].ArrBegin(); keyIt != trackRef["keys"].ArrEnd(); keyIt++){
if ((*keyIt)["time"].asInt() >= (requestedTime / 10000)){
DTSC::Key keyObj;
int partOffset = 0;
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);
std::deque<DTSC::Key>::iterator nextIt = keyIt;
nextIt++;
if (nextIt != trackRef.keys.end()){
keyDur = nextIt->getTime() - keyIt->getTime();
}else{
keyDur = -1;
}
break;
}
partOffset += keyIt->getParts();
}
sstream << "t " << myRef["trackid"].asInt() << "\n";
sstream << "s " << keyObj["time"].asInt() << "\n";
sstream << "p " << keyObj["time"].asInt() + keyObj["len"].asInt() << "\n";
// sstream << "o\n";
sstream << "t " << myRef.trackID << "\n";
sstream << "s " << keyObj.getTime() << "\n";
if (keyDur != -1){
sstream << "p " << keyObj.getTime() + keyDur << "\n";
}else{
sstream << "p\n";
}
ss.SendNow(sstream.str().c_str());
std::cerr << "[[]]Requested " << sstream.str() << std::endl;
unsigned int myDuration;
//Wrap everything in mp4 boxes
MP4::MFHD mfhd_box;
mfhd_box.setSequenceNumber(keyObj["num"].asInt());
myDuration = keyObj["len"].asInt() * 10000;
mfhd_box.setSequenceNumber(keyObj.getNumber());
myDuration = keyObj.getLength() * 10000;
MP4::TFHD tfhd_box;
tfhd_box.setFlags(MP4::tfhdSampleFlag);
tfhd_box.setTrackID(1);
//if (trackRef["type"].asString() == "video"){
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
//}else{
// tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noKeySample);
//}
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
MP4::TRUN trun_box;
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);
}else{
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);
std::deque<long long int> tmpParts;
JSON::decodeVector(keyObj["parts"].asString(), tmpParts);
for (int i = 0; i < tmpParts.size(); i++){
for (int i = 0; i < keyObj.getParts(); i++){
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.
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);
}
MP4::SDTP sdtp_box;
sdtp_box.setVersion(0);
if (trackRef["type"].asString() == "video"){
if (trackRef.type == "video"){
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);
}
}else{
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);
}
}
@ -394,22 +402,24 @@ namespace Connector_HTTP {
traf_box.setContent(sdtp_box, 2);
//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
MP4::UUID_TrackFragmentReference fragref_box;
fragref_box.setVersion(1);
fragref_box.setFragmentCount(0);
int fragCount = 0;
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)){
fragref_box.setTime(fragCount, trackRef["keys"][i]["time"].asInt() * 10000);
fragref_box.setDuration(fragCount, trackRef["keys"][i]["len"].asInt() * 10000);
for (int i = 0; i < 2 && i < trackRef.keys.size() - 1; i++){//< trackRef["keys"].size() - 1; i++){
if (trackRef.keys[i].getTime() > (requestedTime / 10000)){
fragref_box.setTime(fragCount, trackRef.keys[i].getTime() * 10000);
fragref_box.setDuration(fragCount, trackRef.keys[i].getLength() * 10000);
fragref_box.setFragmentCount(++fragCount);
}
}
traf_box.setContent(fragref_box, 3);
}
MP4::MOOF moof_box;
moof_box.setContent(mfhd_box, 0);
moof_box.setContent(traf_box, 1);
@ -423,7 +433,7 @@ namespace Connector_HTTP {
HTTP_S.SetHeader("Content-Type", "video/mp4");
HTTP_S.StartResponse(HTTP_R, 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("mdat", 4, conn);
handlingRequest = true;
@ -465,6 +475,8 @@ namespace Connector_HTTP {
handlingRequest = false;
}
}
}else{
Util::sleep(10);
}
if ( !ss.connected()){
break;

View file

@ -107,23 +107,16 @@ namespace Connector_HTTP {
if(trackID == -1){
// 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++){
if (objIt->second["codec"].asStringRef() == "srt"){
trackID = objIt->second["trackid"].asInt();
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
if (it->second.codec == "srt"){
trackID = it->second.trackID;
subtitleTrack = true;
break;
}
}
}else{
// 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++){
if (objIt->second["trackid"].asInt() == trackID){
subtitleTrack = (objIt->second["codec"].asStringRef() == "srt");
break;
}else{
subtitleTrack = false;
}
}
subtitleTrack = Strm.metadata.tracks[trackID].codec == "srt";
}
if(!subtitleTrack){
@ -139,7 +132,7 @@ namespace Connector_HTTP {
cmd << "t " << trackID;
int maxTime = Strm.metadata["lastms"].asInt();
int maxTime = Strm.metadata.tracks[trackID].lastms;
cmd << "\ns " << seek_time << "\np " << maxTime << "\n";
ss.SendNow(cmd.str().c_str(), cmd.str().size());

View file

@ -33,14 +33,14 @@ int main(int argc, char ** argv){
}
long long int lastStats = 0;
long long int started = Util::epoch();
while (std::cout.good()){
while (std::cout.good() && S.connected()){
if (S.spool()){
while (S.Received().size()){
std::cout.write(S.Received().get().c_str(), S.Received().get().size());
S.Received().get().clear();
}
}else{
Util::sleep(10); //sleep 10ms if no data
Util::sleep(500); //sleep 500ms if no data
}
unsigned int now = Util::epoch();
if (now != lastStats){

View file

@ -568,12 +568,12 @@ namespace Connector_RTMP {
ss.setBlocking(false);
Strm.waitForMeta(ss);
//find first audio and video tracks
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
if (videoID == -1 && objIt->second["type"].asStringRef() == "video"){
videoID = objIt->second["trackid"].asInt();
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->second.trackID;
}
if (audioID == -1 && objIt->second["type"].asStringRef() == "audio"){
audioID = objIt->second["trackid"].asInt();
if (audioID == -1 && it->second.type == "audio"){
audioID = it->second.trackID;
}
}
//select the tracks and play
@ -612,7 +612,7 @@ namespace Connector_RTMP {
amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
sendCommand(amfreply, playMessageType, playStreamId);
//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
}
//send streambegin
@ -638,14 +638,14 @@ namespace Connector_RTMP {
//sent init data if needed
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));
if (audioID != -1 && Strm.getTrackById(audioID).isMember("init")){
init_tag.DTSCAudioInit(Strm.getTrackById(audioID));
if (audioID != -1){
init_tag.DTSCAudioInit(Strm.metadata.tracks[audioID]);
Socket.SendNow(RTMPStream::SendMedia(init_tag));
}
if (videoID != -1 && Strm.getTrackById(videoID).isMember("init")){
init_tag.DTSCVideoInit(Strm.getTrackById(videoID));
if (videoID != -1){
init_tag.DTSCVideoInit(Strm.metadata.tracks[videoID]);
Socket.SendNow(RTMPStream::SendMedia(init_tag));
}
streamInited = true;

View file

@ -61,36 +61,25 @@ namespace Connector_TS {
}
if(trackIDs == ""){
std::stringstream tmpTracks;
// no track ids given? Find the first video and first audio track (if available) and use those!
int videoID = -1;
int audioID = -1;
Strm.waitForMeta(ss);
if (Strm.metadata.isMember("tracks")){
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;
}
for (JSON::ObjIter trackIt = Strm.metadata["tracks"].ObjBegin(); trackIt != Strm.metadata["tracks"].ObjEnd(); trackIt++){
if (videoID == -1 && it->second.type == "video"){
videoID = it->first;
tmpTracks << " " << it->first;
}
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")
} // for iterator
trackIDs += tmpTracks.str();
} // if trackIDs == ""
std::string cmd = "t " + trackIDs + "\ns 0\np\n";
@ -115,7 +104,7 @@ namespace Connector_TS {
char * ContCounter = 0;
if (Strm.lastType() == DTSC::VIDEO){
if ( !haveAvcc){
avccbox.setPayload(Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["init"].asString());
avccbox.setPayload(Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].init);
haveAvcc = true;
}
@ -139,7 +128,7 @@ namespace Connector_TS {
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
ContCounter = &VideoCounter;
}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.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();

View file

@ -153,8 +153,8 @@ namespace Controller {
float onemin, fivemin, fifteenmin;
if (sscanf(line, "%f %f %f", &onemin, &fivemin, &fifteenmin) == 3){
capa["load"]["one"] = (long long int)(onemin * 100);
capa["load"]["five"] = (long long int)(onemin * 100);
capa["load"]["fifteen"] = (long long int)(onemin * 100);
capa["load"]["five"] = (long long int)(fivemin * 100);
capa["load"]["fifteen"] = (long long int)(fifteenmin * 100);
}
}

View file

@ -33,25 +33,25 @@ namespace Converters {
if (Strm.parsePacket(inBuffer)){
if ( !doneheader){
//find first audio and video tracks
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
if (videoID == -1 && objIt->second["type"].asString() == "video"){
videoID = objIt->second["trackid"].asInt();
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 && objIt->second["type"].asString() == "audio"){
audioID = objIt->second["trackid"].asInt();
if (audioID == -1 && it->second.type == "audio"){
audioID = it->first;
}
}
doneheader = true;
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);
if (videoID && Strm.getTrackById(videoID).isMember("init")){
FLV_out.DTSCVideoInit(Strm.getTrackById(videoID));
if (videoID != -1){
FLV_out.DTSCVideoInit(Strm.metadata.tracks[videoID]);
std::cout.write(FLV_out.data, FLV_out.len);
}
if (audioID && Strm.getTrackById(audioID).isMember("init")){
FLV_out.DTSCAudioInit(Strm.getTrackById(audioID));
if (audioID != -1){
FLV_out.DTSCAudioInit(Strm.metadata.tracks[audioID]);
std::cout.write(FLV_out.data, FLV_out.len);
}
}

View file

@ -24,11 +24,12 @@ namespace Converters {
int DTSC2MP4(Util::Config & conf){
DTSC::File input(conf.getString("filename"));//DTSC input
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
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());
}
input.selectTracks(selector);

View file

@ -20,7 +20,7 @@ namespace Converters{
char* curNewPayload;
OGG::headerPages oggMeta;
//Creating ID headers for theora and vorbis
oggMeta.readDTSCHeader(DTSCFile.getMeta());
oggMeta.readDTSCHeader(DTSCFile.getMeta().toJSON());
std::cout << oggMeta.parsedPages;//outputting header pages
//create DTSC in OGG pages

View file

@ -32,10 +32,22 @@ namespace Converters {
MP4::AVCC avccbox;
bool haveAvcc = false;
std::stringstream TSBuf;
int videoID = -1;
int audioID = -1;
while (std::cin.good()){
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){
TSBuf.flush();
if (TSBuf.str().size()){
@ -46,7 +58,7 @@ namespace Converters {
TSBuf.str("");
}
if ( !haveAvcc){
avccbox.setPayload(Strm.metadata["video"]["init"].asString());
avccbox.setPayload(Strm.metadata.tracks[videoID].init);
haveAvcc = true;
}
if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
@ -83,7 +95,7 @@ namespace Converters {
PIDno = 0x100;
ContCounter = &VideoCounter;
}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.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
PIDno = 0x101;

View file

@ -8,19 +8,6 @@
///\brief Holds everything unique to 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.
///\param conf The current configuration of the program.
///\return The return code for the fixed program.
@ -29,232 +16,39 @@ namespace Converters {
F.seek_bpos(0);
F.parseNext();
JSON::Value oriheader = F.getJSON();
JSON::Value meta = F.getMeta();
JSON::Value pack;
DTSC::Meta meta(F.getMeta());
if ( !oriheader.isMember("moreheader")){
std::cerr << "This file is too old to fix - please reconvert." << std::endl;
return 1;
}
if (DTSC::isFixed(meta) && !conf.getBool("force")){
if (meta.isFixed() && !conf.getBool("force")){
std::cerr << "This file was already fixed or doesn't need fixing - cancelling." << std::endl;
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();
while ( !F.getJSON().isNull()){
currentID = "";
if (F.getJSON()["trackid"].asInt() == 0){
if (F.getJSON()["datatype"].asString() == "video"){
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.getJSON()["bpos"] = bPos;
meta.update(F.getJSON());
bPos = F.getBytePos();
F.parseNext();
}
long long int firstms = 0x7fffffff;
long long int lastms = -1;
for (std::map<std::string,HeaderEntryDTSC>::iterator it = trackData.begin(); it != trackData.end(); it++){
if (it->second.firstms < firstms){
firstms = it->second.firstms;
for (std::map<int,DTSC::Track>::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it++){
if (it->second.fragments.size()){
it->second.fragments.rbegin()->setDuration(it->second.fragments.rbegin()->getDuration() - it->second.lastms);
}
if (it->second.lastms > lastms){
lastms = it->second.lastms;
if (it->second.lastms / 1000){
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
std::string loader = meta.toPacked();
long long int newHPos = F.addHeader(loader);
if ( !newHPos){
std::string loader = meta.toJSON().toPacked();
oriheader["moreheader"] = F.addHeader(loader);
if ( !oriheader["moreheader"].asInt()){
std::cerr << "Failure appending new header." << std::endl;
return -1;
}
//re-write the original header with information about the location of the new one
oriheader["moreheader"] = newHPos;
loader = oriheader.toPacked();
if (F.writeHeader(loader)){
return 0;

View file

@ -57,7 +57,7 @@ namespace Converters {
fullSort = false;
}else{
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;
return 1;
}
@ -69,11 +69,11 @@ namespace Converters {
outFile = DTSC::File(outFileName, true);
}else{
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;
return 1;
}
meta = outFile.getMeta();
meta = outFile.getMeta().toJSON();
newMeta = meta;
if (meta.isMember("tracks") && meta["tracks"].size() > 0){
for (JSON::ObjIter trackIt = meta["tracks"].ObjBegin(); trackIt != meta["tracks"].ObjEnd(); trackIt++){
@ -86,7 +86,7 @@ namespace Converters {
std::multimap<int,keyframeInfo> allSorted;
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){
for (JSON::ObjIter trackIt = tmpMeta["tracks"].ObjBegin(); trackIt != tmpMeta["tracks"].ObjEnd(); trackIt++){
long long int oldID = trackIt->second["trackid"].asInt();

View file

@ -15,7 +15,7 @@ namespace Info {
return 1;
}
DTSC::File F(argv[1]);
JSON::Value fileSpecs = F.getMeta();
JSON::Value fileSpecs = F.getMeta().toJSON();
if( !fileSpecs ) {
char ** cmd = (char**)malloc(3*sizeof(char*));
cmd[0] = (char*)"ffprobe";
@ -95,8 +95,9 @@ namespace Info {
}
JSON::Value tracks = fileSpecs["tracks"];
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("parts");
}
}
printf( "%s", fileSpecs.toString().c_str() );