From 521c19f932d7b51886aee7fd119557265bee844c Mon Sep 17 00:00:00 2001
From: Thulinma <jaron@vietors.com>
Date: Fri, 28 Sep 2012 18:56:15 +0200
Subject: [PATCH] Several bugfixes (HTTP dynamic seeking!) and updates to the
 MP4 lib.

---
 src/analysers/Makefile.am       |   3 +-
 src/analysers/dtsc_analyser.cpp |   3 +-
 src/analysers/mp4_analyser.cpp  |  29 ++++++
 src/conn_http_dynamic.cpp       | 165 +++++++++++++++++++-------------
 src/controller.cpp              |   2 +-
 src/converters/dtscfix.cpp      |  37 ++++---
 src/converters/flv2dtsc.cpp     |   4 +-
 src/player.cpp                  |  34 ++++---
 8 files changed, 177 insertions(+), 100 deletions(-)
 create mode 100644 src/analysers/mp4_analyser.cpp

diff --git a/src/analysers/Makefile.am b/src/analysers/Makefile.am
index 4f31f0e9..0a7c1f7e 100644
--- a/src/analysers/Makefile.am
+++ b/src/analysers/Makefile.am
@@ -1,7 +1,8 @@
 AM_CPPFLAGS = $(global_CFLAGS) $(MIST_CFLAGS)
 LDADD = $(MIST_LIBS)
-bin_PROGRAMS=MistAnalyserRTMP MistAnalyserFLV MistAnalyserDTSC MistAnalyserAMF
+bin_PROGRAMS=MistAnalyserRTMP MistAnalyserFLV MistAnalyserDTSC MistAnalyserAMF MistAnalyserMP4
 MistAnalyserRTMP_SOURCES=rtmp_analyser.cpp
 MistAnalyserFLV_SOURCES=flv_analyser.cpp
 MistAnalyserDTSC_SOURCES=dtsc_analyser.cpp
 MistAnalyserAMF_SOURCES=amf_analyser.cpp
+MistAnalyserMP4_SOURCES=mp4_analyser.cpp
diff --git a/src/analysers/dtsc_analyser.cpp b/src/analysers/dtsc_analyser.cpp
index c355586c..a0f5a21a 100644
--- a/src/analysers/dtsc_analyser.cpp
+++ b/src/analysers/dtsc_analyser.cpp
@@ -13,8 +13,7 @@ int main(int argc, char ** argv){
   conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Filename of the DTSC file to analyse.\"}"));
   conf.parseArgs(argc, argv);
   DTSC::File F(conf.getString("filename"));
-  std::string loader = F.getHeader();
-  JSON::Value meta = JSON::fromDTMI(loader);
+  JSON::Value meta = F.getMeta();
   std::cout << meta.toPrettyString() << std::endl;
   JSON::Value pack;
 
diff --git a/src/analysers/mp4_analyser.cpp b/src/analysers/mp4_analyser.cpp
new file mode 100644
index 00000000..19ad4292
--- /dev/null
+++ b/src/analysers/mp4_analyser.cpp
@@ -0,0 +1,29 @@
+/// \file mp4_analyser.cpp
+/// Debugging tool for MP4 data.
+/// Expects MP4 data through stdin, outputs human-readable information to stderr.
+
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <string.h>
+#include <mist/mp4.h>
+#include <mist/config.h>
+
+/// Debugging tool for MP4 data.
+/// Expects MP4 data through stdin, outputs human-readable information to stderr.
+int main(int argc, char ** argv) {
+  Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION);
+  conf.parseArgs(argc, argv);
+
+  std::string temp;
+  while (std::cin.good()){temp += std::cin.get();}//read all of std::cin to temp
+  temp.erase(temp.size()-1, 1);//strip the invalid last character
+
+  MP4::Box mp4data;
+  while (mp4data.read(temp)){
+    std::cerr << mp4data.toPrettyString(0) << std::endl;
+  }
+  return 0;
+}
+
diff --git a/src/conn_http_dynamic.cpp b/src/conn_http_dynamic.cpp
index d711a7a7..7edddbc5 100644
--- a/src/conn_http_dynamic.cpp
+++ b/src/conn_http_dynamic.cpp
@@ -26,64 +26,94 @@
 /// Holds everything unique to HTTP Dynamic Connector.
 namespace Connector_HTTP{
 
-  std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata, int fragnum, int starttime){
-    MP4::AFRT afrt;
-    if (starttime == 0){
-      afrt.SetUpdate(false);
-    }else{
-      afrt.SetUpdate(true);
-    }
-    afrt.SetTimeScale(1000);
-    afrt.AddQualityEntry("");
-    if (!metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){
-      //metadata["lasttime"].asInt()?
-      afrt.AddFragmentRunEntry(fragnum, starttime, 2000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
-    }else{
-      afrt.AddFragmentRunEntry(fragnum, starttime, metadata["video"]["keyms"].asInt()); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
-    }
-    afrt.WriteContent();
-    
+  std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata, int fragnum, int starttime, int endtime){
+    std::string empty;
+
     MP4::ASRT asrt;
     if (starttime == 0){
-      asrt.SetUpdate(false);
+      asrt.setUpdate(false);
     }else{
-      asrt.SetUpdate(true);
+      asrt.setUpdate(true);
+    }
+    asrt.setVersion(1);
+    asrt.setQualityEntry(empty, 0);
+    if (!metadata.isMember("keytime") || metadata["keytime"].size() == 0){
+      asrt.setSegmentRun(1, 20000, 0);
+    }else{
+      asrt.setSegmentRun(1, metadata["keytime"].size(), 0);
+    }
+
+
+    MP4::AFRT afrt;
+    if (starttime == 0){
+      afrt.setUpdate(false);
+    }else{
+      afrt.setUpdate(true);
+    }
+    afrt.setVersion(1);
+    afrt.setTimeScale(1000);
+    afrt.setQualityEntry(empty, 0);
+    MP4::afrt_runtable afrtrun;
+    if (!metadata.isMember("keytime") || metadata["keytime"].size() == 0){
+      afrtrun.firstFragment = 1;
+      afrtrun.firstTimestamp = 0;
+      if (!metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){
+        afrtrun.duration = 2000;
+      }else{
+        afrtrun.duration = metadata["video"]["keyms"].asInt();
+      }
+      afrt.setFragmentRun(afrtrun, 0);
+    }else{
+      for (int i = 0; i < metadata["keytime"].size(); i++){
+        afrtrun.firstFragment = i+1;
+        afrtrun.firstTimestamp = metadata["keytime"][i].asInt();
+        if (i+1 < metadata["keytime"].size()){
+          afrtrun.duration = metadata["keytime"][i+1].asInt() - metadata["keytime"][i].asInt();
+        }else{
+          if (metadata["lastms"].asInt()){
+            afrtrun.duration = metadata["lastms"].asInt() - metadata["keytime"][i].asInt();
+          }else{
+            afrtrun.duration = 3000;//guess 3 seconds if unknown
+          }
+        }
+        afrt.setFragmentRun(afrtrun, i);
+      }
     }
-    asrt.AddQualityEntry("");
-    /// \todo Actually use correct number of fragments.
-    asrt.AddSegmentRunEntry(1, 20000);//1 Segment, 20000 Fragments
-    asrt.WriteContent();
     
     MP4::ABST abst;
-    abst.AddFragmentRunTable(&afrt);
-    abst.AddSegmentRunTable(&asrt);
-    abst.SetBootstrapVersion(1);
-    abst.SetProfile(0);
-    if (metadata.isMember("length") && metadata["length"].asInt() > 0){
-      abst.SetLive(false);
-      abst.SetMediaTime(1000*metadata["length"].asInt());
-    }else{
-      abst.SetLive(true);
-      abst.SetMediaTime(0xFFFFFFFF);//metadata["lasttime"].asInt()?
-    }
+    abst.setVersion(1);
+    abst.setBootstrapinfoVersion(1);
+    abst.setProfile(0);
     if (starttime == 0){
-      abst.SetUpdate(false);
+      abst.setUpdate(false);
     }else{
-      abst.SetUpdate(true);
+      abst.setUpdate(true);
     }
-    abst.SetTimeScale(1000);
-    abst.SetSMPTE(0);
-    abst.SetMovieIdentifier(MovieId);
-    abst.SetDRM("");
-    abst.SetMetaData("");
-    abst.AddServerEntry("");
-    abst.AddQualityEntry("");
-    abst.WriteContent();
+    abst.setTimeScale(1000);
+    if (metadata.isMember("length") && metadata["length"].asInt() > 0){
+      abst.setLive(false);
+      if (metadata["lastms"].asInt()){
+        abst.setCurrentMediaTime(metadata["lastms"].asInt());
+      }else{
+        abst.setCurrentMediaTime(1000*metadata["length"].asInt());
+      }
+    }else{
+      abst.setLive(true);
+      abst.setCurrentMediaTime(0xFFFFFFFF);
+    }
+    abst.setSmpteTimeCodeOffset(0);
+    abst.setMovieIdentifier(MovieId);
+    abst.setServerEntry(empty, 0);
+    abst.setQualityEntry(empty, 0);
+    abst.setDrmData(empty);
+    abst.setMetaData(empty);
+    abst.setSegmentRunTable(asrt, 0);
+    abst.setFragmentRunTable(afrt, 0);
 
     //#if DEBUG >= 8
     std::cout << "Sending bootstrap:" << std::endl << abst.toPrettyString(0) << std::endl;
     //#endif
-    return std::string((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize());
+    return std::string((char*)abst.asBox(), (int)abst.boxedSize());
   }
   
 
@@ -91,18 +121,19 @@ namespace Connector_HTTP{
   std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){
     std::string Result;
     if (metadata.isMember("length") && metadata["length"].asInt() > 0){
-      std::stringstream st;
-      st << ((double)metadata["video"]["keyms"].asInt() / 1000);
       Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
       "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n"
       "<id>" + MovieId + "</id>\n"
-      "<duration>" + metadata["length"].asString() + "</duration>\n"
+      "<width>" + metadata["video"]["width"].asString() + "</width>\n"
+      "<height>" + metadata["video"]["height"].asString() + "</height>\n"
+      "<duration>" + metadata["length"].asString() + ".000</duration>\n"
       "<mimeType>video/mp4</mimeType>\n"
       "<streamType>recorded</streamType>\n"
       "<deliveryType>streaming</deliveryType>\n"
-      "<bestEffortFetchInfo segmentDuration=\""+metadata["length"].asString()+".000\" fragmentDuration=\""+st.str()+"\" />\n"
-      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0)) + "</bootstrapInfo>\n"
-      "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n"
+      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "</bootstrapInfo>\n"
+      "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\">\n"
+      "<metadata>AgAKb25NZXRhRGF0YQgAAAAAAAl0cmFja2luZm8KAAAAAgMACXRpbWVzY2FsZQBA+GoAAAAAAAAGbGVuZ3RoAEGMcHoQAAAAAAhsYW5ndWFnZQIAA2VuZwARc2FtcGxlZGVzY3JpcHRpb24KAAAAAQMACnNhbXBsZXR5cGUCAARhdmMxAAAJAAAJAwAJdGltZXNjYWxlAEDncAAAAAAAAAZsZW5ndGgAQXtNvTAAAAAACGxhbmd1YWdlAgADZW5nABFzYW1wbGVkZXNjcmlwdGlvbgoAAAABAwAKc2FtcGxldHlwZQIABG1wNGEAAAkAAAkADWF1ZGlvY2hhbm5lbHMAQAAAAAAAAAAAD2F1ZGlvc2FtcGxlcmF0ZQBA53AAAAAAAAAOdmlkZW9mcmFtZXJhdGUAQDf/gi5SciUABmFhY2FvdABAAAAAAAAAAAAIYXZjbGV2ZWwAQD8AAAAAAAAACmF2Y3Byb2ZpbGUAQFNAAAAAAAAADGF1ZGlvY29kZWNpZAIABG1wNGEADHZpZGVvY29kZWNpZAIABGF2YzEABXdpZHRoAECQ4AAAAAAAAAZoZWlnaHQAQIMAAAAAAAAACmZyYW1lV2lkdGgAQJDgAAAAAAAAC2ZyYW1lSGVpZ2h0AECDAAAAAAAAAAxkaXNwbGF5V2lkdGgAQJDgAAAAAAAADWRpc3BsYXlIZWlnaHQAQIMAAAAAAAAADG1vb3Zwb3NpdGlvbgBBmxq2uAAAAAAIZHVyYXRpb24AQIKjqW3oyhIAAAk=</metadata>\n"
+      "</media>\n"
       "</manifest>\n";
     }else{
       Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
@@ -111,7 +142,7 @@ namespace Connector_HTTP{
       "<mimeType>video/mp4</mimeType>\n"
       "<streamType>live</streamType>\n"
       "<deliveryType>streaming</deliveryType>\n"
-      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0)) + "</bootstrapInfo>\n"
+      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "</bootstrapInfo>\n"
       "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n"
       "</manifest>\n";
     }
@@ -273,26 +304,26 @@ namespace Connector_HTTP{
                 #if DEBUG >= 3
                 fprintf(stderr, "Sending a fragment...");
                 #endif
-                static std::string btstrp;
-                btstrp = GenerateBootstrap(streamname, Strm.metadata, ReqFragment, FlashBufTime);
+                //static std::string btstrp;
+                //btstrp = GenerateBootstrap(streamname, Strm.metadata, ReqFragment, FlashBufTime, Strm.getPacket(0)["time"]);
                 HTTP_S.Clean();
                 HTTP_S.SetHeader("Content-Type", "video/mp4");
                 HTTP_S.SetBody("");
-                HTTP_S.SetHeader("Content-Length", FlashBufSize+32+33+btstrp.size());
+                HTTP_S.SetHeader("Content-Length", FlashBufSize+8);//32+33+btstrp.size());
                 conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
-                conn.SendNow("\x00\x00\x00\x21" "afra\x00\x00\x00\x00\x00\x00\x00\x03\xE8\x00\x00\x00\x01", 21);
-                unsigned long tmptime = htonl(FlashBufTime << 32);
-                conn.SendNow((char*)&tmptime, 4);
-                tmptime = htonl(FlashBufTime & 0xFFFFFFFF);
-                conn.SendNow((char*)&tmptime, 4);
-                tmptime = htonl(65);
-                conn.SendNow((char*)&tmptime, 4);
+                //conn.SendNow("\x00\x00\x00\x21" "afra\x00\x00\x00\x00\x00\x00\x00\x03\xE8\x00\x00\x00\x01", 21);
+                //unsigned long tmptime = htonl(FlashBufTime << 32);
+                //conn.SendNow((char*)&tmptime, 4);
+                //tmptime = htonl(FlashBufTime & 0xFFFFFFFF);
+                //conn.SendNow((char*)&tmptime, 4);
+                //tmptime = htonl(65);
+                //conn.SendNow((char*)&tmptime, 4);
 
-                conn.SendNow(btstrp);
+                //conn.SendNow(btstrp);
 
-                conn.SendNow("\x00\x00\x00\x18moof\x00\x00\x00\x10mfhd\x00\x00\x00\x00", 20);
-                unsigned long fragno = htonl(ReqFragment);
-                conn.SendNow((char*)&fragno, 4);
+                //conn.SendNow("\x00\x00\x00\x18moof\x00\x00\x00\x10mfhd\x00\x00\x00\x00", 20);
+                //unsigned long fragno = htonl(ReqFragment);
+                //conn.SendNow((char*)&fragno, 4);
                 unsigned long size = htonl(FlashBufSize+8);
                 conn.SendNow((char*)&size, 4);
                 conn.SendNow("mdat", 4);
@@ -313,11 +344,13 @@ namespace Connector_HTTP{
                 //fill buffer with init data, if needed.
                 if (Strm.metadata.isMember("audio") && Strm.metadata["audio"].isMember("init")){
                   tmp.DTSCAudioInit(Strm);
+                  tmp.tagTime(Strm.getPacket(0)["time"].asInt());
                   FlashBuf.push_back(std::string(tmp.data, tmp.len));
                   FlashBufSize += tmp.len;
                 }
                 if (Strm.metadata.isMember("video") && Strm.metadata["video"].isMember("init")){
                   tmp.DTSCVideoInit(Strm);
+                  tmp.tagTime(Strm.getPacket(0)["time"].asInt());
                   FlashBuf.push_back(std::string(tmp.data, tmp.len));
                   FlashBufSize += tmp.len;
                 }
diff --git a/src/controller.cpp b/src/controller.cpp
index c6ebbfc6..fc3213b7 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -215,7 +215,6 @@ void CheckConfig(JSON::Value & in, JSON::Value & out){
     }
   }
   out = in;
-  out["version"] = PACKAGE_VERSION;
 }
 
 bool streamsEqual(JSON::Value & one, JSON::Value & two){
@@ -558,6 +557,7 @@ int main(int argc, char ** argv){
                   //sent current configuration, no matter if it was changed or not
                   //Response["streams"] = Storage["streams"];
                   Response["config"] = Connector::Storage["config"];
+                  Response["config"]["version"] = PACKAGE_VERSION "/" + Util::Config::libver;
                   Response["streams"] = Connector::Storage["streams"];
                   //add required data to the current unix time to the config, for syncing reasons
                   Response["config"]["time"] = Util::epoch();
diff --git a/src/converters/dtscfix.cpp b/src/converters/dtscfix.cpp
index fabddc75..29c6c49d 100644
--- a/src/converters/dtscfix.cpp
+++ b/src/converters/dtscfix.cpp
@@ -12,10 +12,21 @@ namespace Converters{
   /// Reads an DTSC file and attempts to fix the metadata in it.
   int DTSCFix(Util::Config & conf) {
     DTSC::File F(conf.getString("filename"));
-    std::string loader = F.getHeader();
-    JSON::Value meta = JSON::fromDTMI(loader);
+    JSON::Value oriheader = F.getMeta();
+    JSON::Value meta = oriheader;
     JSON::Value pack;
 
+    if (!oriheader.isMember("moreheader")){
+      std::cerr << "This file is not DTSCFix'able. Please re-convert and try again." << std::endl;
+      return 1;
+    }
+    if (oriheader["moreheader"].asInt() > 0){
+      std::cerr << "Warning: This file has already been DTSCFix'ed. Doing this multiple times makes the file larger for no reason." << std::endl;
+    }
+    meta.removeMember("keytime");
+    meta.removeMember("keybpos");
+    meta.removeMember("moreheader");
+
     long long unsigned int firstpack = 0;
     long long unsigned int nowpack = 0;
     long long unsigned int lastaudio = 0;
@@ -54,6 +65,8 @@ namespace Converters{
           if (bps > vid_max){vid_max = bps;}
         }
         if (F.getJSON()["keyframe"].asInt() != 0){
+          meta["keytime"].append(F.getJSON()["time"]);
+          meta["keybpos"].append(F.getLastReadPos());
           if (lastkey != 0){
             bps = nowpack - lastkey;
             if (bps < key_min){key_min = bps;}
@@ -73,12 +86,10 @@ namespace Converters{
       F.seekNext();
     }
 
-    std::cout << std::endl << "Summary:" << std::endl;
     meta["length"] = (long long int)((nowpack - firstpack)/1000);
+    meta["lastms"] = (long long int)nowpack;
     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));
@@ -88,16 +99,20 @@ namespace Converters{
       }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;
     }
 
+    std::cerr << "Appending new header..." << std::endl;
+    std::string loader = meta.toPacked();
+    long long int newHPos = F.addHeader(loader);
+    if (!newHPos){
+      std::cerr << "Failure appending new header. Cancelling." << std::endl;
+      return 1;
+    }
     std::cerr << "Re-writing header..." << std::endl;
-
-    loader = meta.toPacked();
+    oriheader["moreheader"] = newHPos;
+    loader = oriheader.toPacked();
     if (F.writeHeader(loader)){
+      std::cerr << "Metadata is now: " << meta.toPrettyString(0) << std::endl;
       return 0;
     }else{
       return -1;
diff --git a/src/converters/flv2dtsc.cpp b/src/converters/flv2dtsc.cpp
index d8d7f04e..efea24fe 100644
--- a/src/converters/flv2dtsc.cpp
+++ b/src/converters/flv2dtsc.cpp
@@ -36,6 +36,7 @@ namespace Converters{
           counter++;
           if (counter > 8){
             sending = true;
+            meta_out["moreheader"] = 0LL;
             std::string packed_header = meta_out.toPacked();
             unsigned int size = htonl(packed_header.size());
             std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header;
@@ -59,12 +60,13 @@ namespace Converters{
     // if the FLV input is very short, do output it correctly...
     if (!sending){
       std::cerr << "EOF - outputting buffer..." << std::endl;
+      meta_out["moreheader"] = 0LL;
       std::string packed_header = meta_out.toPacked();
       unsigned int size = htonl(packed_header.size());
       std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header;
       std::cout << prebuffer.rdbuf();
     }
-    std::cerr << "Done!" << std::endl;
+    std::cerr << "Done! If you output this data to a file, don't forget to run MistDTSCFix next." << std::endl;
     
     return 0;
   }//FLV2DTSC
diff --git a/src/player.cpp b/src/player.cpp
index 12e1f5e7..260b42c1 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -69,7 +69,7 @@ int main(int argc, char** argv){
 
   DTSC::File source = DTSC::File(conf.getString("filename"));
   Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin));
-  std::string meta_str = source.getHeader();
+  JSON::Value meta = source.getMeta();
   JSON::Value pausemark;
   pausemark["datatype"] = "pause_marker";
   pausemark["time"] = (long long int)0;
@@ -78,14 +78,9 @@ int main(int argc, char** argv){
   int lasttime = Util::epoch();//time last packet was sent
 
   //send the header
-  {
-    in_out.Send("DTSC");
-    unsigned int size = htonl(meta_str.size());
-    in_out.Send((char*)&size, 4);
-    in_out.Send(meta_str);
-  }
+  std::string meta_str = meta.toNetPacked();
+  in_out.Send(meta_str);
 
-  JSON::Value meta = JSON::fromDTMI(meta_str);
   if (meta["video"]["keyms"].asInt() < 11){
     meta["video"]["keyms"] = (long long int)1000;
   }
@@ -132,6 +127,10 @@ int main(int argc, char** argv){
                 json_sts["vod"]["start"] = Util::epoch() - sts.conntime;
                 if (!meta_sent){
                   json_sts["vod"]["meta"] = meta;
+                  json_sts["vod"]["meta"]["audio"].removeMember("init");
+                  json_sts["vod"]["meta"]["video"].removeMember("init");
+                  json_sts["vod"]["meta"].removeMember("keytime");
+                  json_sts["vod"]["meta"].removeMember("keybpos");
                   meta_sent = true;
                 }
                 StatsSocket.Send(json_sts.toString().c_str());
@@ -181,17 +180,16 @@ int main(int argc, char** argv){
           }
           lastTime = now;
           if (playing > 0){--playing;}
-          if (playing == 0){
-            #if DEBUG >= 4
-            std::cerr << "Sending pause_marker (" << (Util::getMS() - bench) << "ms)" << std::endl;
-            #endif
-            pausemark["time"] = (long long int)now;
-            pausemark.toPacked();
-            in_out.SendNow(pausemark.toNetPacked());
-            in_out.setBlocking(true);
-          }
         }
-        if (playing != 0){
+        if (playing == 0){
+          #if DEBUG >= 4
+          std::cerr << "Sending pause_marker (" << (Util::getMS() - bench) << "ms)" << std::endl;
+          #endif
+          pausemark["time"] = source.getJSON()["time"];
+          pausemark.toPacked();
+          in_out.SendNow(pausemark.toNetPacked());
+          in_out.setBlocking(true);
+        }else{
           lasttime = Util::epoch();
           //insert proper header for this type of data
           in_out.Send("DTPD");