From a304d017d7b807c0e78ac5d1a425ffbf3a99925e Mon Sep 17 00:00:00 2001
From: Oswald Auguste de Bruin <oswald.de.bruin@ddvtech.com>
Date: Thu, 22 Aug 2013 14:09:53 +0200
Subject: [PATCH] Started on mp4 progressive

---
 src/Makefile.am                              |   2 +
 src/connectors/conn_http_progressive_mp4.cpp | 232 +++++++++++++++++++
 src/converters/dtsc2mp4.cpp                  |  20 +-
 3 files changed, 250 insertions(+), 4 deletions(-)
 create mode 100644 src/connectors/conn_http_progressive_mp4.cpp

diff --git a/src/Makefile.am b/src/Makefile.am
index c48424eb..5e96502c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,6 +18,7 @@ bin_PROGRAMS+=MistConnRTMP
 bin_PROGRAMS+=MistConnHTTP 
 bin_PROGRAMS+=MistConnHTTPProgressiveFLV 
 bin_PROGRAMS+=MistConnHTTPProgressiveMP3 
+bin_PROGRAMS+=MistConnHTTPProgressiveMP4 
 bin_PROGRAMS+=MistConnHTTPProgressiveOGG 
 bin_PROGRAMS+=MistConnHTTPDynamic 
 bin_PROGRAMS+=MistConnHTTPSmooth 
@@ -57,6 +58,7 @@ MistConnHTTP_SOURCES=connectors/conn_http.cpp tinythread.cpp tinythread.h ../VER
 MistConnHTTP_LDADD=$(MIST_LIBS) -lpthread
 MistConnHTTPProgressiveFLV_SOURCES=connectors/conn_http_progressive_flv.cpp ../VERSION
 MistConnHTTPProgressiveMP3_SOURCES=connectors/conn_http_progressive_mp3.cpp ../VERSION
+MistConnHTTPProgressiveMP4_SOURCES=connectors/conn_http_progressive_mp4.cpp ../VERSION
 MistConnHTTPProgressiveOGG_SOURCES=connectors/conn_http_progressive_ogg.cpp ../VERSION
 MistConnHTTPDynamic_SOURCES=connectors/conn_http_dynamic.cpp ../VERSION
 MistConnHTTPSmooth_SOURCES=connectors/conn_http_smooth.cpp ../VERSION
diff --git a/src/connectors/conn_http_progressive_mp4.cpp b/src/connectors/conn_http_progressive_mp4.cpp
new file mode 100644
index 00000000..80dd3cfd
--- /dev/null
+++ b/src/connectors/conn_http_progressive_mp4.cpp
@@ -0,0 +1,232 @@
+///\file conn_http_progressive.cpp
+///\brief Contains the main code for the HTTP Progressive Connector
+
+#include <iostream>
+#include <queue>
+#include <sstream>
+
+#include <cstdlib>
+#include <cstdio>
+#include <cmath>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <mist/socket.h>
+#include <mist/http_parser.h>
+#include <mist/dtsc.h>
+#include <mist/ogg.h>
+#include <mist/amf.h>
+#include <mist/config.h>
+#include <mist/stream.h>
+#include <mist/timing.h>
+
+///\brief Holds everything unique to HTTP Connectors.
+namespace Connector_HTTP {
+  ///\brief Main function for the HTTP Progressive Connector
+  ///\param conn A socket describing the connection the client.
+  ///\return The exit code of the connector.
+  int progressiveConnector(Socket::Connection conn){
+    bool progressive_has_sent_header = false;//Indicates whether we have sent a header.
+    bool ready4data = false; //Set to true when streaming is to begin.
+    DTSC::Stream Strm; //Incoming stream buffer.
+    HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.
+    bool inited = false;//Whether the stream is initialized
+    Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream.
+    std::string streamname;//Will contain the name of the stream.
+
+    //MP4 specific variables
+
+    //OGG specific variables
+    /*OGG::headerPages oggMeta;
+    OGG::Page curOggPage;
+    std::map <long long unsigned int, std::vector<JSON::Value> > DTSCBuffer;
+    std::map <long long unsigned int, long long unsigned int> prevGran;*/
+    
+    
+    unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
+    unsigned int seek_sec = 0;//Seek position in ms
+    unsigned int seek_byte = 0;//Seek position in bytes
+    
+    int videoID = -1;
+    int audioID = -1;
+
+    while (conn.connected()){
+      //Only attempt to parse input when not yet init'ed.
+      if ( !inited){
+        if (conn.Received().size() || conn.spool()){
+          //make sure it ends in a \n
+          if ( *(conn.Received().get().rbegin()) != '\n'){
+            std::string tmp = conn.Received().get();
+            conn.Received().get().clear();
+            if (conn.Received().size()){
+              conn.Received().get().insert(0, tmp);
+            }else{
+              conn.Received().append(tmp);
+            }
+          }
+          if (HTTP_R.Read(conn.Received().get())){
+#if DEBUG >= 5
+            std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
+#endif
+            conn.setHost(HTTP_R.GetHeader("X-Origin"));
+            //we assume the URL is the stream name with a 3 letter extension
+            streamname = HTTP_R.getUrl().substr(1);
+            size_t extDot = streamname.rfind('.');
+            if (extDot != std::string::npos){
+              streamname.resize(extDot);
+            }; //strip the extension
+            int start = 0;
+            if ( !HTTP_R.GetVar("start").empty()){
+              start = atoi(HTTP_R.GetVar("start").c_str());
+            }
+            if ( !HTTP_R.GetVar("starttime").empty()){
+              start = atoi(HTTP_R.GetVar("starttime").c_str());
+            }
+            if ( !HTTP_R.GetVar("apstart").empty()){
+              start = atoi(HTTP_R.GetVar("apstart").c_str());
+            }
+            if ( !HTTP_R.GetVar("ec_seek").empty()){
+              start = atoi(HTTP_R.GetVar("ec_seek").c_str());
+            }
+            if ( !HTTP_R.GetVar("fs").empty()){
+              start = atoi(HTTP_R.GetVar("fs").c_str());
+            }
+            //under 3 hours we assume seconds, otherwise byte position
+            if (start < 10800){
+              seek_sec = start * 1000; //ms, not s
+            }else{
+              seek_byte = start; //divide by 1mbit, then *1000 for ms.
+            }
+            ready4data = true;
+            HTTP_R.Clean(); //clean for any possible next requests
+          }
+        }
+      }
+      if (ready4data){
+        if ( !inited){
+          //we are ready, connect the socket!
+          ss = Util::Stream::getStream(streamname);
+          if ( !ss.connected()){
+#if DEBUG >= 1
+            fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str());
+#endif
+            ss.close();
+            HTTP_S.Clean();
+            HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
+            conn.SendNow(HTTP_S.BuildResponse("404", "Not found"));
+            ready4data = false;
+            continue;
+          }
+          //wait until we have a header
+          while ( !Strm.metadata && ss.connected()){
+            if (ss.spool()){
+              Strm.parsePacket(ss.Received()); //read the metadata
+            }else{
+              Util::sleep(5);
+            }
+          }
+          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();
+            }
+            if (audioID == -1 && objIt->second["type"].asString() == "audio"){
+              audioID = objIt->second["trackid"].asInt();
+            }
+          }
+          if (videoID != -1){
+            byterate += Strm.getTrackById(videoID)["bps"].asInt();
+          }
+          if (audioID != -1){
+            byterate += Strm.getTrackById(audioID)["bps"].asInt();
+          }
+          if ( !byterate){byterate = 1;}
+          seek_sec = (seek_byte / byterate) * 1000;
+          std::stringstream cmd;
+          cmd << "t";
+          if (videoID != -1){
+            cmd << " " << videoID;
+          }
+          if (audioID != -1){
+            cmd << " " << audioID;
+          }
+          cmd << "\ns " << seek_sec << "\np\n";
+          ss.SendNow(cmd.str().c_str(), cmd.str().size());
+          inited = true;
+        }
+        unsigned int now = Util::epoch();
+        if (now != lastStats){
+          lastStats = now;
+          ss.SendNow(conn.getStats("HTTP_Progressive_Ogg").c_str());
+        }
+        if (ss.spool()){
+          while (Strm.parsePacket(ss.Received())){
+            
+            if ( !progressive_has_sent_header){
+              HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers
+              HTTP_S.SetHeader("Content-Type", "video/ogg"); //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
+              //Fill in header here
+              progressive_has_sent_header = true;
+            }
+            //parse DTSC to MP4 here
+          }
+        }else{
+          Util::sleep(1);
+        }
+        if ( !ss.connected()){
+          break;
+        }
+      }
+    }
+    conn.close();
+    ss.SendNow(conn.getStats("HTTP_Dynamic").c_str());
+    ss.close();
+    return 0;
+  } //Progressive_Connector main function
+
+} //Connector_HTTP namespace
+
+///\brief The standard process-spawning main function.
+int main(int argc, char ** argv){
+  Util::Config conf(argv[0], PACKAGE_VERSION);
+  JSON::Value capa;
+  capa["desc"] = "Enables HTTP protocol progressive streaming.";
+  capa["deps"] = "HTTP";
+  capa["url_rel"] = "/$.mp4";
+  capa["url_match"] = "/$.mp4";
+  capa["url_handler"] = "http";
+  capa["url_type"] = "mp4";
+  capa["socket"] = "http_progressive_mp4";
+  conf.addBasicConnectorOptions(capa);
+  conf.parseArgs(argc, argv);
+  
+  if (conf.getBool("json")){
+    std::cout << capa.toString() << std::endl;
+    return -1;
+  }
+  
+  Socket::Server server_socket = Socket::Server("/tmp/mist/http_progressive_mp4");
+  if ( !server_socket.connected()){
+    return 1;
+  }
+  conf.activate();
+
+  while (server_socket.connected() && conf.is_active){
+    Socket::Connection S = server_socket.accept();
+    if (S.connected()){ //check if the new connection is valid
+      pid_t myid = fork();
+      if (myid == 0){ //if new child, start MAINHANDLER
+        return Connector_HTTP::progressiveConnector(S);
+      }else{ //otherwise, do nothing or output debugging text
+#if DEBUG >= 5
+        fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
+#endif
+      }
+    }
+  } //while connected
+  server_socket.close();
+  return 0;
+} //main
diff --git a/src/converters/dtsc2mp4.cpp b/src/converters/dtsc2mp4.cpp
index a2fdff75..6bb44f50 100644
--- a/src/converters/dtsc2mp4.cpp
+++ b/src/converters/dtsc2mp4.cpp
@@ -46,6 +46,7 @@ namespace Converters {
     std::cout << std::string(ftypBox.asBox(),ftypBox.boxedSize());
     
     uint64_t mdatSize = 0;
+    //std::vector<uint64_t> stcoOffsets;
     //moov box
     MP4::MOOV moovBox;
       MP4::MVHD mvhdBox;
@@ -262,7 +263,7 @@ namespace Converters {
                 stblBox.setContent(stszBox,3 + offset);
                   
                 MP4::STCO stcoBox;
-                stcoBox.setVersion(0);
+                stcoBox.setVersion(1);
                 total = 0;
                 uint64_t totalByteOffset = 0;
                 //Inserting wrong values on purpose here, will be fixed later.
@@ -278,9 +279,17 @@ namespace Converters {
                     totalByteOffset += keyParts[i].size;
                   }
                 }
+                //calculating the offset where the STCO box will be in the main MOOV box
+                //needed for probable optimise
+                /*stcoOffsets.push_back(
+                  moovBox.payloadSize() +
+                  trakBox.boxedSize() +
+                  mdiaBox.boxedSize() +
+                  minfBox.boxedSize() +
+                  stblBox.boxedSize()
+                );*/
                 mdatSize = totalByteOffset;
                 stblBox.setContent(stcoBox,4 + offset);
-
               minfBox.setContent(stblBox,2);
             mdiaBox.setContent(minfBox, 2);
           trakBox.setContent(mdiaBox, 1);
@@ -288,10 +297,10 @@ namespace Converters {
         boxOffset++;
       }
       //end arbitrary
-      //initial offset lengte ftyp, length moov + 8
+      //initial offset length ftyp, length moov + 8
       unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8;
       //update all STCO
-      std::map <int, MP4::STCO&> STCOFix; 
+      //std::map <int, MP4::STCO&> STCOFix; 
       //for tracks
       for (unsigned int i = 1; i < moovBox.getContentCount(); i++){
         //10 lines to get the STCO box.
@@ -326,6 +335,9 @@ namespace Converters {
             break;
           }
         }
+        /*MP4::Box temp = MP4::Box((moovBox.payload()+stcoOffsets[i]),false);
+        MP4::STCO & checkStcoBox = *((MP4::STCO*)(&temp));
+        std::cerr << checkStcoBox.toPrettyString() << std::endl;*/
         //got the STCO box, fixing values with MP4 header offset
         for (int j = 0; j < checkStcoBox.getEntryCount(); j++){
           checkStcoBox.setChunkOffset(checkStcoBox.getChunkOffset(j) + byteOffset, j);