Added HTTP Live Connector, for HLS. Streams bipbop video up till approximately 40 seconds properly, needs to be optimized further for other videos. Audio is buggy still.
This commit is contained in:
		
							parent
							
								
									acc1c1acdb
								
							
						
					
					
						commit
						0bb5a342e1
					
				
					 1 changed files with 509 additions and 0 deletions
				
			
		
							
								
								
									
										509
									
								
								src/conn_http_live.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										509
									
								
								src/conn_http_live.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,509 @@
 | 
				
			||||||
 | 
					#define DEBUG 5
 | 
				
			||||||
 | 
					/// \file conn_http_dynamic.cpp
 | 
				
			||||||
 | 
					/// Contains the main code for the HTTP Dynamic Connector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <queue>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <cstdio>
 | 
				
			||||||
 | 
					#include <cmath>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <sys/wait.h>
 | 
				
			||||||
 | 
					#include <getopt.h>
 | 
				
			||||||
 | 
					#include <mist/socket.h>
 | 
				
			||||||
 | 
					#include <mist/http_parser.h>
 | 
				
			||||||
 | 
					#include <mist/json.h>
 | 
				
			||||||
 | 
					#include <mist/dtsc.h>
 | 
				
			||||||
 | 
					#include <mist/mp4.h>
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <mist/stream.h>
 | 
				
			||||||
 | 
					#include <mist/timing.h>
 | 
				
			||||||
 | 
					#include <mist/ts_packet.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Holds everything unique to HTTP Connectors.
 | 
				
			||||||
 | 
					namespace Connector_HTTP {
 | 
				
			||||||
 | 
					  /// Parses the list of keyframes into 10 second fragments
 | 
				
			||||||
 | 
					  std::vector<int> keyframesToFragments( JSON::Value & metadata ) {
 | 
				
			||||||
 | 
					    std::vector<int> result;
 | 
				
			||||||
 | 
					    if( metadata.isNull() ) {
 | 
				
			||||||
 | 
					      return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    result.push_back(0);
 | 
				
			||||||
 | 
					    int currentBase = 0;
 | 
				
			||||||
 | 
					    for (int i = 0; i < metadata["keytime"].size(); i++){
 | 
				
			||||||
 | 
					      if ((metadata["keytime"][i].asInt() - currentBase) > 10000){
 | 
				
			||||||
 | 
					        currentBase = metadata["keytime"][i].asInt();
 | 
				
			||||||
 | 
					        result.push_back(i);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Returns a m3u or m3u8 index file
 | 
				
			||||||
 | 
					  std::string BuildIndex(std::string & MovieId, JSON::Value & metadata){
 | 
				
			||||||
 | 
					    std::stringstream Result;
 | 
				
			||||||
 | 
					    std::vector<int> fragIndices = keyframesToFragments( metadata );
 | 
				
			||||||
 | 
					    int longestFragment = 0;
 | 
				
			||||||
 | 
					    for (int i = 1; i < fragIndices.size(); i++){
 | 
				
			||||||
 | 
					      int fragDuration = metadata["keytime"][fragIndices[i]].asInt() - metadata["keytime"][fragIndices[i-1]].asInt();
 | 
				
			||||||
 | 
					      if (fragDuration > longestFragment){
 | 
				
			||||||
 | 
					        longestFragment = fragDuration;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (metadata.isMember("length") && metadata["length"].asInt() > 0){
 | 
				
			||||||
 | 
					      Result <<
 | 
				
			||||||
 | 
					          "#EXTM3U\r\n"
 | 
				
			||||||
 | 
					              "#EXT-X-VERSION:3\r\n"
 | 
				
			||||||
 | 
					              "#EXT-X-MEDIA-SEQUENCE:1\r\n"
 | 
				
			||||||
 | 
					              //"#EXT-X-ALLOW-CACHE:YES\r\n"
 | 
				
			||||||
 | 
					              "#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n"
 | 
				
			||||||
 | 
					              "#EXT-X-PLAYLIST-TYPE:VOD\r\n";
 | 
				
			||||||
 | 
					      int lastDuration = 0;
 | 
				
			||||||
 | 
					      int lastBytePos = 0;
 | 
				
			||||||
 | 
					      bool writeOffset = true;
 | 
				
			||||||
 | 
					      int j = 0;
 | 
				
			||||||
 | 
					      for (int i = 0; i < fragIndices.size(); i++) {
 | 
				
			||||||
 | 
					        if (lastDuration != 0){
 | 
				
			||||||
 | 
					          Result << "#EXTINF:" << (metadata["keytime"][fragIndices[i]].asInt() - lastDuration) / 1000 << "." << std::setw(3) << std::setfill('0') << ((metadata["keytime"][fragIndices[i]].asInt() - lastDuration) % 1000) << ",\r\n"
 | 
				
			||||||
 | 
					          << ++j << ".ts\r\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        lastDuration = metadata["keytime"][fragIndices[i]].asInt();
 | 
				
			||||||
 | 
					        lastBytePos = metadata["keybpos"][fragIndices[i]].asInt();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      Result << "#EXT-X-ENDLIST";
 | 
				
			||||||
 | 
					    }else{
 | 
				
			||||||
 | 
					      Result <<
 | 
				
			||||||
 | 
					          "#EXTM3U\r\n"
 | 
				
			||||||
 | 
					              "#EXT-X-VERSION:4\r\n"
 | 
				
			||||||
 | 
					              "#EXT-X-MEDIA-SEQUENCE:1\r\n"
 | 
				
			||||||
 | 
					              "#EXT-X-TARGETDURATION:" << ((metadata["video"]["keyms"].asInt() + metadata["video"]["keyvar"].asInt()) / 1000) + 1 << "\r\n";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#if DEBUG >= 8
 | 
				
			||||||
 | 
					    std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    return Result.str();
 | 
				
			||||||
 | 
					  } //BuildIndex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Main function for Connector_HTTP_Live
 | 
				
			||||||
 | 
					  int Connector_HTTP_Live(Socket::Connection conn){
 | 
				
			||||||
 | 
					    std::stringstream TSBuf;
 | 
				
			||||||
 | 
					    long long int TSBufTime = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DTSC::Stream Strm; //Incoming stream buffer.
 | 
				
			||||||
 | 
					    HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver en HTTP Sender.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool ready4data = false; //Set to true when streaming is to begin.
 | 
				
			||||||
 | 
					    bool pending_manifest = false;
 | 
				
			||||||
 | 
					    bool receive_marks = false; //when set to true, this stream will ignore keyframes and instead use pause marks
 | 
				
			||||||
 | 
					    bool inited = false;
 | 
				
			||||||
 | 
					    Socket::Connection ss( -1);
 | 
				
			||||||
 | 
					    std::string streamname;
 | 
				
			||||||
 | 
					    std::string recBuffer = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string ToPack;
 | 
				
			||||||
 | 
					    TS::Packet PackData;
 | 
				
			||||||
 | 
					    std::string DTMIData;
 | 
				
			||||||
 | 
					    int PacketNumber = 0;
 | 
				
			||||||
 | 
					    long long unsigned int TimeStamp = 0;
 | 
				
			||||||
 | 
					    int ThisNaluSize;
 | 
				
			||||||
 | 
					    char VideoCounter = 0;
 | 
				
			||||||
 | 
					    char AudioCounter = 0;
 | 
				
			||||||
 | 
					    bool WritePesHeader;
 | 
				
			||||||
 | 
					    bool IsKeyFrame;
 | 
				
			||||||
 | 
					    bool FirstKeyFrame = true;
 | 
				
			||||||
 | 
					    bool FirstIDRInKeyFrame;
 | 
				
			||||||
 | 
					    MP4::AVCC avccbox;
 | 
				
			||||||
 | 
					    bool haveAvcc = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    std::vector<int> fragIndices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int Segment = -1;
 | 
				
			||||||
 | 
					    int temp;
 | 
				
			||||||
 | 
					    int Flash_RequestPending = 0;
 | 
				
			||||||
 | 
					    unsigned int lastStats = 0;
 | 
				
			||||||
 | 
					    conn.setBlocking(false); //do not block on conn.spool() when no data is available
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (conn.connected()){
 | 
				
			||||||
 | 
					      if (conn.spool() || conn.Received().size()){
 | 
				
			||||||
 | 
					        //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 >= 4
 | 
				
			||||||
 | 
					          std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					          conn.setHost(HTTP_R.GetHeader("X-Origin"));
 | 
				
			||||||
 | 
					          if (HTTP_R.url.find(".m3u") == std::string::npos){
 | 
				
			||||||
 | 
					            streamname = HTTP_R.url.substr(5, HTTP_R.url.find("/", 5) - 5);
 | 
				
			||||||
 | 
					            if ( !ss){
 | 
				
			||||||
 | 
					              ss = Util::Stream::getStream(streamname);
 | 
				
			||||||
 | 
					              if ( !ss.connected()){
 | 
				
			||||||
 | 
					#if DEBUG >= 1
 | 
				
			||||||
 | 
					                fprintf(stderr, "Could not connect to server!\n");
 | 
				
			||||||
 | 
					#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;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              ss.setBlocking(false);
 | 
				
			||||||
 | 
					              inited = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            temp = HTTP_R.url.find("/", 5) + 1;
 | 
				
			||||||
 | 
					            Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str());
 | 
				
			||||||
 | 
					            if( !fragIndices.size() ) {
 | 
				
			||||||
 | 
					              fragIndices = keyframesToFragments( Strm.metadata );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if DEBUG >= 4
 | 
				
			||||||
 | 
					            fprintf( stderr, "Fragment number %d\n", Segment);
 | 
				
			||||||
 | 
					            fprintf( stderr, "Fragment indices %d\n", fragIndices.size() );
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::stringstream sstream;
 | 
				
			||||||
 | 
					            sstream << "f " << Segment + 1 << "\n";
 | 
				
			||||||
 | 
					            int frameCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (Segment == fragIndices.size() - 1){
 | 
				
			||||||
 | 
					              frameCount = Strm.metadata["keytime"].size() - fragIndices[Segment];
 | 
				
			||||||
 | 
					            }else{
 | 
				
			||||||
 | 
					              frameCount = Strm.metadata["keytime"][fragIndices[Segment+1]].asInt() - Strm.metadata["keytime"][fragIndices[Segment]].asInt();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					#if DEBUG >= 4
 | 
				
			||||||
 | 
					            fprintf( stderr, "Frame count %d\n", frameCount);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					            for (int i = 0; i < frameCount; i++){
 | 
				
			||||||
 | 
					              sstream << "o \n";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ss.SendNow(sstream.str().c_str());
 | 
				
			||||||
 | 
					            Flash_RequestPending++;
 | 
				
			||||||
 | 
					          }else{
 | 
				
			||||||
 | 
					            streamname = HTTP_R.url.substr(5, HTTP_R.url.find("/", 5) - 5);
 | 
				
			||||||
 | 
					            if ( !Strm.metadata.isNull()){
 | 
				
			||||||
 | 
					              HTTP_S.Clean();
 | 
				
			||||||
 | 
					              if (HTTP_R.url.find(".m3u8") != std::string::npos) {
 | 
				
			||||||
 | 
					                HTTP_S.SetHeader("Content-Type", "application/vnd.apple.mpegurl");//m3u8
 | 
				
			||||||
 | 
					              }else{
 | 
				
			||||||
 | 
					                HTTP_S.SetHeader("Content-Type", "audio/mpegurl");//m3u
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              HTTP_S.SetHeader("Cache-Control", "no-cache");
 | 
				
			||||||
 | 
					              if (Strm.metadata.isMember("length")){
 | 
				
			||||||
 | 
					                receive_marks = true;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              std::string manifest = BuildIndex(streamname, Strm.metadata);
 | 
				
			||||||
 | 
					              HTTP_S.SetBody(manifest);
 | 
				
			||||||
 | 
					              conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
 | 
				
			||||||
 | 
					#if DEBUG >= 3
 | 
				
			||||||
 | 
					              printf("Sent manifest\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					              pending_manifest = false;
 | 
				
			||||||
 | 
					            }else{
 | 
				
			||||||
 | 
					              pending_manifest = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          ready4data = true;
 | 
				
			||||||
 | 
					          HTTP_R.Clean(); //clean for any possible next requests
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }else{
 | 
				
			||||||
 | 
					        if (Flash_RequestPending){
 | 
				
			||||||
 | 
					          usleep(1000); //sleep 1ms
 | 
				
			||||||
 | 
					        }else{
 | 
				
			||||||
 | 
					          usleep(10000); //sleep 10ms
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      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!\n");
 | 
				
			||||||
 | 
					#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;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          ss.setBlocking(false);
 | 
				
			||||||
 | 
					#if DEBUG >= 3
 | 
				
			||||||
 | 
					          fprintf(stderr, "Everything connected, starting to send video data...\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					          inited = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        unsigned int now = Util::epoch();
 | 
				
			||||||
 | 
					        if (now != lastStats){
 | 
				
			||||||
 | 
					          lastStats = now;
 | 
				
			||||||
 | 
					          ss.SendNow(conn.getStats("HTTP_Live").c_str());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (ss.spool()){
 | 
				
			||||||
 | 
					          while (Strm.parsePacket(ss.Received())){
 | 
				
			||||||
 | 
					            if (Strm.getPacket(0).isMember("time")){
 | 
				
			||||||
 | 
					              if ( !Strm.metadata.isMember("firsttime")){
 | 
				
			||||||
 | 
					                Strm.metadata["firsttime"] = Strm.getPacket(0)["time"];
 | 
				
			||||||
 | 
					              }else{
 | 
				
			||||||
 | 
					                if ( !Strm.metadata.isMember("length") || Strm.metadata["length"].asInt() == 0){
 | 
				
			||||||
 | 
					                  Strm.getPacket(0)["time"] = Strm.getPacket(0)["time"].asInt() - Strm.metadata["firsttime"].asInt();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              Strm.metadata["lasttime"] = Strm.getPacket(0)["time"];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (pending_manifest){
 | 
				
			||||||
 | 
					              HTTP_S.Clean();
 | 
				
			||||||
 | 
					              HTTP_S.SetHeader("Cache-Control", "no-cache");
 | 
				
			||||||
 | 
					              if (Strm.metadata.isMember("length")){
 | 
				
			||||||
 | 
					                receive_marks = true;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              std::string manifest = BuildIndex(streamname, Strm.metadata);
 | 
				
			||||||
 | 
					              if (HTTP_R.url.find(".m3u8") != std::string::npos) {
 | 
				
			||||||
 | 
					                HTTP_S.SetHeader("Content-Type", "application/vnd.apple.mpegurl");//m3u8
 | 
				
			||||||
 | 
					              }else{
 | 
				
			||||||
 | 
					                HTTP_S.SetHeader("Content-Type", "audio/mpegurl");//m3u
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              HTTP_S.SetBody(manifest);
 | 
				
			||||||
 | 
					              conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
 | 
				
			||||||
 | 
					#if DEBUG >= 3
 | 
				
			||||||
 | 
					              printf("Sent manifest\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					              pending_manifest = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ( !receive_marks && Strm.metadata.isMember("length")){
 | 
				
			||||||
 | 
					              receive_marks = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ((Strm.getPacket(0).isMember("keyframe") && !receive_marks) || Strm.lastType() == DTSC::PAUSEMARK){
 | 
				
			||||||
 | 
					#if DEBUG >= 4
 | 
				
			||||||
 | 
					              fprintf(stderr, "Received a %s fragment of %i bytes.\n", Strm.getPacket(0)["datatype"].asString().c_str(), TSBuf.rdbuf()->in_avail());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					              if (Flash_RequestPending > 0 && TSBuf.rdbuf()->in_avail()){
 | 
				
			||||||
 | 
					#if DEBUG >= 3
 | 
				
			||||||
 | 
					                fprintf(stderr, "Sending a fragment...");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					                HTTP_S.Clean();
 | 
				
			||||||
 | 
					                HTTP_S.SetHeader("Content-Type", "video/MP2T");
 | 
				
			||||||
 | 
					                HTTP_S.SetBody("");
 | 
				
			||||||
 | 
					                HTTP_S.SetHeader("Content-Length", TSBuf.rdbuf()->in_avail());
 | 
				
			||||||
 | 
					                conn.SendNow(HTTP_S.BuildResponse("200", "OK"));                
 | 
				
			||||||
 | 
					                conn.SendNow(TSBuf.str().c_str(),TSBuf.rdbuf()->in_avail());
 | 
				
			||||||
 | 
					                TSBuf.clear();
 | 
				
			||||||
 | 
					                Flash_RequestPending--;
 | 
				
			||||||
 | 
					                PacketNumber = 0;
 | 
				
			||||||
 | 
					#if DEBUG >= 3
 | 
				
			||||||
 | 
					                fprintf(stderr, "Done\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              TSBuf.clear();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if( !haveAvcc ) {
 | 
				
			||||||
 | 
					              avccbox.setPayload( Strm.metadata["video"]["init"].asString() );
 | 
				
			||||||
 | 
					              haveAvcc = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
 | 
				
			||||||
 | 
					              if( Strm.lastType() == DTSC::VIDEO ) {
 | 
				
			||||||
 | 
					                DTMIData = Strm.lastData();
 | 
				
			||||||
 | 
					                if( Strm.getPacket(0).isMember("keyframe") ) {
 | 
				
			||||||
 | 
					                  IsKeyFrame = true;
 | 
				
			||||||
 | 
					                  FirstIDRInKeyFrame = true;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                  IsKeyFrame = false;
 | 
				
			||||||
 | 
					                  FirstKeyFrame = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if( IsKeyFrame ) {
 | 
				
			||||||
 | 
					                  TimeStamp = ( Strm.getPacket(0)["time"].asInt() * 27000 );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                int TSType;
 | 
				
			||||||
 | 
					                bool FirstPic = true;
 | 
				
			||||||
 | 
					                bool haveInit = false;
 | 
				
			||||||
 | 
					                std::string videoBuffer;
 | 
				
			||||||
 | 
					                while( DTMIData.size() ) {
 | 
				
			||||||
 | 
					                  ThisNaluSize =  (DTMIData[0] << 24) + (DTMIData[1] << 16) +
 | 
				
			||||||
 | 
					                                  (DTMIData[2] << 8) + DTMIData[3];
 | 
				
			||||||
 | 
					                  DTMIData.erase(0,4);//Erase the first four characters;
 | 
				
			||||||
 | 
					                  TSType = (int)DTMIData[0] & 0x1F;
 | 
				
			||||||
 | 
					                  if( TSType == 0x09 ) {
 | 
				
			||||||
 | 
					                    DTMIData.erase(0,ThisNaluSize);
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  if( TSType == 0x07 || TSType == 0x08 ) {
 | 
				
			||||||
 | 
					                    haveInit = true;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  if( TSType == 0x05 ) {
 | 
				
			||||||
 | 
					                    if( FirstPic ) {
 | 
				
			||||||
 | 
					                      if( !haveInit ) {
 | 
				
			||||||
 | 
					                        ToPack += avccbox.asAnnexB( );
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                      FirstPic = false;
 | 
				
			||||||
 | 
					                    } 
 | 
				
			||||||
 | 
					                    if( IsKeyFrame ) {
 | 
				
			||||||
 | 
					                      if( !FirstKeyFrame && FirstIDRInKeyFrame ) {
 | 
				
			||||||
 | 
					                        ToPack += (char)0x00;
 | 
				
			||||||
 | 
					                        FirstIDRInKeyFrame = false;
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                      ToPack.append(TS::ShortNalHeader,3);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                  } else if ( TSType == 0x01 ) {
 | 
				
			||||||
 | 
					                    if( FirstPic ) {
 | 
				
			||||||
 | 
					                      ToPack += (char)0x00;
 | 
				
			||||||
 | 
					                      FirstPic = false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    ToPack.append(TS::ShortNalHeader,3);
 | 
				
			||||||
 | 
					                  } else {
 | 
				
			||||||
 | 
					                    ToPack.append(TS::NalHeader,4);
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  ToPack.append(DTMIData,0,ThisNaluSize);
 | 
				
			||||||
 | 
					                  DTMIData.erase(0,ThisNaluSize);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                WritePesHeader = true;
 | 
				
			||||||
 | 
					                while( ToPack.size() ) {
 | 
				
			||||||
 | 
					                  if (PacketNumber == 0) {
 | 
				
			||||||
 | 
					                    PackData.DefaultPAT();
 | 
				
			||||||
 | 
					                    TSBuf << std::string(PackData.ToString(), 188);
 | 
				
			||||||
 | 
					                    PackData.DefaultPMT();
 | 
				
			||||||
 | 
					                    TSBuf << std::string(PackData.ToString(), 188);
 | 
				
			||||||
 | 
					                    PacketNumber += 2;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  PackData.Clear();
 | 
				
			||||||
 | 
					                  PackData.PID( 0x100 );
 | 
				
			||||||
 | 
					                  PackData.ContinuityCounter( VideoCounter );
 | 
				
			||||||
 | 
					                  VideoCounter ++;
 | 
				
			||||||
 | 
					                  if( WritePesHeader ) {
 | 
				
			||||||
 | 
					                    PackData.UnitStart( 1 );
 | 
				
			||||||
 | 
					                    if( IsKeyFrame ) {
 | 
				
			||||||
 | 
					                      PackData.RandomAccess( 1 );
 | 
				
			||||||
 | 
					                      PackData.PCR( TimeStamp );
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                      PackData.AdaptationField( 1 );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    PackData.AddStuffing( 184 - (20+ToPack.size()) );
 | 
				
			||||||
 | 
					                    PackData.PESVideoLeadIn( ToPack.size(), Strm.getPacket(0)["time"].asInt() * 90 );
 | 
				
			||||||
 | 
					                    WritePesHeader = false;
 | 
				
			||||||
 | 
					                  } else {
 | 
				
			||||||
 | 
					                    PackData.AdaptationField( 1 );
 | 
				
			||||||
 | 
					                    PackData.AddStuffing( 184 - (ToPack.size()) );
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  PackData.FillFree( ToPack );
 | 
				
			||||||
 | 
					                  TSBuf << std::string(PackData.ToString(), 188);
 | 
				
			||||||
 | 
					                  PacketNumber ++;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              } else if( Strm.lastType() == DTSC::AUDIO ) {
 | 
				
			||||||
 | 
					                WritePesHeader = true;
 | 
				
			||||||
 | 
					                std::string audioBuffer = TS::GetAudioHeader( DTMIData.size(), Strm.metadata["audio"]["init"].asString() );
 | 
				
			||||||
 | 
					                int fullSize = Strm.lastData().size() + audioBuffer.size();
 | 
				
			||||||
 | 
					                int currentOffset = 0;
 | 
				
			||||||
 | 
					                while( fullSize ) {
 | 
				
			||||||
 | 
					                  if ( PacketNumber == 0 ) {
 | 
				
			||||||
 | 
					                    PackData.DefaultPAT();
 | 
				
			||||||
 | 
					                    TSBuf << std::string(PackData.ToString(), 188);
 | 
				
			||||||
 | 
					                    PackData.DefaultPMT();
 | 
				
			||||||
 | 
					                    TSBuf << std::string(PackData.ToString(), 188);
 | 
				
			||||||
 | 
					                    PacketNumber += 2;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  PackData.Clear();
 | 
				
			||||||
 | 
					                  PackData.PID( 0x101 );
 | 
				
			||||||
 | 
					                  PackData.ContinuityCounter( AudioCounter );
 | 
				
			||||||
 | 
					                  AudioCounter ++;
 | 
				
			||||||
 | 
					                  if( WritePesHeader ) {
 | 
				
			||||||
 | 
					                    PackData.UnitStart( 1 );
 | 
				
			||||||
 | 
					                    PackData.RandomAccess( 0 );
 | 
				
			||||||
 | 
					                    PackData.AddStuffing( 184 - (14 + fullSize) );
 | 
				
			||||||
 | 
					                    PackData.PESAudioLeadIn( fullSize, Strm.getPacket(0)["time"].asInt() * 81000 );
 | 
				
			||||||
 | 
					                    WritePesHeader = false;
 | 
				
			||||||
 | 
					                  } else {
 | 
				
			||||||
 | 
					                    PackData.AdaptationField( 1 );
 | 
				
			||||||
 | 
					                    PackData.AddStuffing( 184 - fullSize );
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  PackData.FillFree( audioBuffer );
 | 
				
			||||||
 | 
					                  if( PackData.BytesFree( ) ) {
 | 
				
			||||||
 | 
					                    currentOffset += PackData.FillFree( Strm.lastData().c_str() + currentOffset, Strm.lastData().size() - currentOffset );
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  TSBuf << std::string(PackData.ToString(), 188);
 | 
				
			||||||
 | 
					                  PacketNumber ++;
 | 
				
			||||||
 | 
					                  fullSize = audioBuffer.size() + (Strm.lastData().size() - currentOffset);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }  
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (pending_manifest && !Strm.metadata.isNull()){
 | 
				
			||||||
 | 
					            HTTP_S.Clean();
 | 
				
			||||||
 | 
					            HTTP_S.SetHeader("Cache-Control", "no-cache");
 | 
				
			||||||
 | 
					            if (Strm.metadata.isMember("length")){
 | 
				
			||||||
 | 
					              receive_marks = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (HTTP_R.url.find(".m3u8") != std::string::npos) {
 | 
				
			||||||
 | 
					              HTTP_S.SetHeader("Content-Type", "application/vnd.apple.mpegurl");//m3u8
 | 
				
			||||||
 | 
					            }else{
 | 
				
			||||||
 | 
					              HTTP_S.SetHeader("Content-Type", "audio/mpegurl");//m3u
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            std::string manifest = BuildIndex(streamname, Strm.metadata);
 | 
				
			||||||
 | 
					            HTTP_S.SetBody(manifest);
 | 
				
			||||||
 | 
					            conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
 | 
				
			||||||
 | 
					#if DEBUG >= 3
 | 
				
			||||||
 | 
					            printf("Sent manifest\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					            pending_manifest = false;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ( !ss.connected()){
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    conn.close();
 | 
				
			||||||
 | 
					    ss.SendNow(conn.getStats("HTTP_Live").c_str());
 | 
				
			||||||
 | 
					    ss.close();
 | 
				
			||||||
 | 
					#if DEBUG >= 1
 | 
				
			||||||
 | 
					    fprintf(stderr, "User %i disconnected.\n", conn.getSocket());
 | 
				
			||||||
 | 
					    if (inited){
 | 
				
			||||||
 | 
					      fprintf(stderr, "Status was: inited\n");
 | 
				
			||||||
 | 
					    }else{
 | 
				
			||||||
 | 
					      if (ready4data){
 | 
				
			||||||
 | 
					        fprintf(stderr, "Status was: ready4data\n");
 | 
				
			||||||
 | 
					      }else{
 | 
				
			||||||
 | 
					        fprintf(stderr, "Status was: connected\n");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  } //Connector_HTTP_Dynamic main function
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} //Connector_HTTP_Dynamic namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
 | 
					  Util::Config conf(argv[0], PACKAGE_VERSION);
 | 
				
			||||||
 | 
					  conf.addConnectorOptions(1935);
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  Socket::Server server_socket = Socket::Server("/tmp/mist/http_live");
 | 
				
			||||||
 | 
					  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::Connector_HTTP_Live(S);
 | 
				
			||||||
 | 
					      }else{ //otherwise, do nothing or output debugging text
 | 
				
			||||||
 | 
					#if DEBUG >= 3
 | 
				
			||||||
 | 
					        fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } //while connected
 | 
				
			||||||
 | 
					  server_socket.close();
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					} //main
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue