Analysers unification
This commit is contained in:
		
							parent
							
								
									19198e3abd
								
							
						
					
					
						commit
						051a8c826b
					
				
					 24 changed files with 2588 additions and 35 deletions
				
			
		| 
						 | 
					@ -266,11 +266,74 @@ macro(makeAnalyser analyserName format)
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
endmacro()
 | 
					endmacro()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
makeAnalyser(RTMP rtmp)
 | 
					add_executable(MistAnalyserMP4 src/analysers/analyser_mp4.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
makeAnalyser(FLV flv)
 | 
					target_link_libraries(MistAnalyserMP4 mist)
 | 
				
			||||||
makeAnalyser(DTSC dtsc)
 | 
					  install(
 | 
				
			||||||
makeAnalyser(AMF amf)
 | 
					    TARGETS MistAnalyserMP4
 | 
				
			||||||
makeAnalyser(MP4 mp4)
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  add_executable(MistAnalyserFLV src/analysers/analyser_flv.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
 | 
					target_link_libraries(MistAnalyserFLV mist)
 | 
				
			||||||
 | 
					  install(
 | 
				
			||||||
 | 
					    TARGETS MistAnalyserFLV
 | 
				
			||||||
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					add_executable(MistAnalyserRTMP src/analysers/analyser_rtmp.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
 | 
					target_link_libraries(MistAnalyserRTMP mist)
 | 
				
			||||||
 | 
					  install(
 | 
				
			||||||
 | 
					    TARGETS MistAnalyserRTMP
 | 
				
			||||||
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#niet nodig?
 | 
				
			||||||
 | 
					add_executable(MistAnalyserRTP src/analysers/analyser_rtp.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
 | 
					target_link_libraries(MistAnalyserRTP mist)
 | 
				
			||||||
 | 
					  install(
 | 
				
			||||||
 | 
					    TARGETS MistAnalyserRTP
 | 
				
			||||||
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_executable(MistAnalyserTS src/analysers/analyser_ts.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
 | 
					target_link_libraries(MistAnalyserTS mist)
 | 
				
			||||||
 | 
					  install(
 | 
				
			||||||
 | 
					    TARGETS MistAnalyserTS
 | 
				
			||||||
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_executable(MistAnalyserDASH src/analysers/analyser_dash.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
 | 
					target_link_libraries(MistAnalyserDASH mist)
 | 
				
			||||||
 | 
					  install(
 | 
				
			||||||
 | 
					    TARGETS MistAnalyserDASH
 | 
				
			||||||
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_executable(MistAnalyserHLS src/analysers/analyser_hls.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
 | 
					target_link_libraries(MistAnalyserHLS mist)
 | 
				
			||||||
 | 
					  install(
 | 
				
			||||||
 | 
					    TARGETS MistAnalyserHLS
 | 
				
			||||||
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_executable(MistAnalyserDTSC src/analysers/analyser_dtsc.cpp src/analysers/analyser.cpp)
 | 
				
			||||||
 | 
					target_link_libraries(MistAnalyserDTSC mist)
 | 
				
			||||||
 | 
					  install(
 | 
				
			||||||
 | 
					    TARGETS MistAnalyserDTSC
 | 
				
			||||||
 | 
					    DESTINATION bin
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					makeAnalyser(AMF amf) #hoeft niet
 | 
				
			||||||
 | 
					makeAnalyser(OGG ogg)
 | 
				
			||||||
 | 
					#makeAnalyser(RTP rtp) #LTS
 | 
				
			||||||
 | 
					makeAnalyser(RTSP rtsp_rtp) #LTS ...
 | 
				
			||||||
 | 
					#makeAnalyser(DTSC dtsc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#makeAnalyser(TS ts) #LTS
 | 
				
			||||||
 | 
					#makeAnalyser(HLS hls) #LTS #url
 | 
				
			||||||
 | 
					#makeAnalyser(RTMP rtmp)
 | 
				
			||||||
 | 
					#makeAnalyser(FLV flv)
 | 
				
			||||||
 | 
					#makeAnalyser(MP4 mp4)
 | 
				
			||||||
makeAnalyser(OGG ogg)
 | 
					makeAnalyser(OGG ogg)
 | 
				
			||||||
makeAnalyser(RAX rax)
 | 
					makeAnalyser(RAX rax)
 | 
				
			||||||
#makeAnalyser(RTP rtp) #LTS
 | 
					#makeAnalyser(RTP rtp) #LTS
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <mist/amf.h>
 | 
					#include <mist/amf.h>
 | 
				
			||||||
#include <mist/config.h>
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///\brief Holds everything unique to the analysers.
 | 
					///\brief Holds everything unique to the analysers.
 | 
				
			||||||
namespace Analysers {
 | 
					namespace Analysers {
 | 
				
			||||||
| 
						 | 
					@ -15,7 +16,22 @@ namespace Analysers {
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Expects AMF data through stdin, outputs human-readable information to stderr.
 | 
					  /// Expects AMF data through stdin, outputs human-readable information to stderr.
 | 
				
			||||||
  ///\return The return code of the analyser.
 | 
					  ///\return The return code of the analyser.
 | 
				
			||||||
  int analyseAMF(){
 | 
					  int analyseAMF(Util::Config conf){
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    std::string filename = conf.getString("filename");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if(filename.length() > 0){
 | 
				
			||||||
 | 
					      int fp = open(filename.c_str(), O_RDONLY);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					      if(fp <= 0){
 | 
				
			||||||
 | 
					        FAIL_MSG("Cannot open file: %s",filename.c_str());
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      dup2(fp, STDIN_FILENO);
 | 
				
			||||||
 | 
					      close(fp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    std::string amfBuffer;
 | 
					    std::string amfBuffer;
 | 
				
			||||||
    //Read all of std::cin to amfBuffer
 | 
					    //Read all of std::cin to amfBuffer
 | 
				
			||||||
    while (std::cin.good()){
 | 
					    while (std::cin.good()){
 | 
				
			||||||
| 
						 | 
					@ -33,8 +49,9 @@ namespace Analysers {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char ** argv){
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
  Util::Config conf = Util::Config(argv[0]);
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("filename", JSON::fromString( "{\"arg_num\":1, \"arg\":\"string\", \"default\":\"\", \"help\":\"Filename of the AMF file to analyse.\"}"));
 | 
				
			||||||
  conf.parseArgs(argc, argv);
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  return Analysers::analyseAMF();
 | 
					  return Analysers::analyseAMF(conf);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										104
									
								
								src/analysers/analyser.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/analysers/analyser.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,104 @@
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					#include <mist/timing.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					analysers::analysers(Util::Config &config) {
 | 
				
			||||||
 | 
					  conf = config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //set default detailLevel
 | 
				
			||||||
 | 
					  detail = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (conf.hasOption("filename")) {
 | 
				
			||||||
 | 
					    fileinput = conf.getString("filename").length() > 0;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    fileinput = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  analyse = conf.getString("mode") == "analyse";
 | 
				
			||||||
 | 
					  validate = conf.getString("mode") == "validate";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (validate) {
 | 
				
			||||||
 | 
					    // conf.getOption("detail", true)[1] = 0ll;
 | 
				
			||||||
 | 
					    detail = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (conf.hasOption("detail")) { detail = conf.getInteger("detail"); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Prepare();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int analysers::doAnalyse() {
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					analysers::~analysers() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void analysers::doValidate() {
 | 
				
			||||||
 | 
					  std::cout << upTime << ", " << finTime << ", " << (finTime - upTime) << ", " << endTime << std::endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool analysers::packetReady() {
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fileinput as stdin
 | 
				
			||||||
 | 
					void analysers::Prepare() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (fileinput) {
 | 
				
			||||||
 | 
					    filename = conf.getString("filename");
 | 
				
			||||||
 | 
					    int fp = open(filename.c_str(), O_RDONLY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (fp <= 0) { 
 | 
				
			||||||
 | 
					      FAIL_MSG("Cannot open file: %s", filename.c_str()); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dup2(fp, STDIN_FILENO);
 | 
				
			||||||
 | 
					    close(fp);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool analysers::hasInput() {
 | 
				
			||||||
 | 
					  //  std::cout << std::cin.good() << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return std::cin.good();
 | 
				
			||||||
 | 
					  //  return !feof(stdin);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int analysers::Run() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if(mayExecute)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    std::cout << "start analyser with detailLevel: " << detail << std::endl;
 | 
				
			||||||
 | 
					    endTime = 0;
 | 
				
			||||||
 | 
					    upTime = Util::bootSecs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (hasInput() && mayExecute) {
 | 
				
			||||||
 | 
					      while (packetReady()) {
 | 
				
			||||||
 | 
					        // std::cout << "in loop..." << std::endl;
 | 
				
			||||||
 | 
					        endTime = doAnalyse();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    finTime = Util::bootSecs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (validate) {
 | 
				
			||||||
 | 
					      // std::cout << upTime << ", " << finTime << ", " << (finTime-upTime) << ", " << endTime << std::endl;
 | 
				
			||||||
 | 
					      doValidate();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void analysers::defaultConfig(Util::Config &conf) {
 | 
				
			||||||
 | 
					  conf.addOption("filename",
 | 
				
			||||||
 | 
					                 JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"default\":\"\", \"help\":\"Filename of the file to analysed.\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to "
 | 
				
			||||||
 | 
					                                          "do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.addOption(
 | 
				
			||||||
 | 
					      "detail",
 | 
				
			||||||
 | 
					      JSON::fromString("{\"long\":\"detail\", \"short\":\"D\", \"arg\":\"num\", \"default\":2, \"help\":\"Detail level of analysis. \"}"));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/analysers/analyser.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/analysers/analyser.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class analysers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  protected:
 | 
				
			||||||
 | 
					    bool fileinput;
 | 
				
			||||||
 | 
					    std::string filename;
 | 
				
			||||||
 | 
					    bool analyse;
 | 
				
			||||||
 | 
					    bool validate;
 | 
				
			||||||
 | 
					    bool mayExecute = true;
 | 
				
			||||||
 | 
					    int detail;
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					    long long int endTime;
 | 
				
			||||||
 | 
					    long long int upTime;
 | 
				
			||||||
 | 
					    long long int finTime;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    bool useBuffer;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    Util::Config conf;
 | 
				
			||||||
 | 
					    analysers(Util::Config &config);
 | 
				
			||||||
 | 
					    ~analysers();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    void Prepare();
 | 
				
			||||||
 | 
					    int Run();
 | 
				
			||||||
 | 
					    virtual int doAnalyse();
 | 
				
			||||||
 | 
					    virtual void doValidate();
 | 
				
			||||||
 | 
					    virtual bool hasInput();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static void defaultConfig(Util::Config & conf);
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    virtual bool packetReady();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										645
									
								
								src/analysers/analyser_dash.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										645
									
								
								src/analysers/analyser_dash.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,645 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "analyser_dash.h"
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					#include <mist/http_parser.h>
 | 
				
			||||||
 | 
					#include <mist/mp4.h>
 | 
				
			||||||
 | 
					#include <mist/mp4_generic.h>
 | 
				
			||||||
 | 
					#include <mist/timing.h>
 | 
				
			||||||
 | 
					#include <set>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OTHER 0x00
 | 
				
			||||||
 | 
					#define VIDEO 0x01
 | 
				
			||||||
 | 
					#define AUDIO 0x02
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// http://cattop:8080/dash/bunny/index.mpd
 | 
				
			||||||
 | 
					// http://balderlaptop:8080/dash/f+short.mp4/index.mpd
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					///\brief simple struct for storage of stream-specific data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					StreamData tempSD; // temp global
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool getDelimBlock(std::string &data, std::string name, size_t &blockStart, size_t &blockEnd, std::string delim) {
 | 
				
			||||||
 | 
					  size_t offset = data.find(name);
 | 
				
			||||||
 | 
					  if (offset == std::string::npos) {
 | 
				
			||||||
 | 
					    return false; // name string not found.
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // expected: delim character BEFORE blockstart.
 | 
				
			||||||
 | 
					  offset--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  blockStart = data.find(delim, offset);
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "offset: %i blockStart: %i ", offset, blockStart);
 | 
				
			||||||
 | 
					  offset = blockStart + 1; // skip single character!
 | 
				
			||||||
 | 
					  blockEnd = data.find(delim, offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "offset: %i blockEnd: %i ", offset, blockEnd);
 | 
				
			||||||
 | 
					  if (blockStart == std::string::npos || blockEnd == std::string::npos) {
 | 
				
			||||||
 | 
					    return false; // no start/end quotes found
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  blockEnd++; // include delim
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "getDelimPos: data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart)  );
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool getValueBlock(std::string &data, std::string name, size_t &blockStart, size_t &blockEnd, std::string delim) {
 | 
				
			||||||
 | 
					  size_t offset = data.find(name);
 | 
				
			||||||
 | 
					  if (offset == std::string::npos) {
 | 
				
			||||||
 | 
					    return false; // name string not found.
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  blockStart = data.find(delim, offset);
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "offset: %i blockStart: %i ", offset, blockStart);
 | 
				
			||||||
 | 
					  blockStart++;        // clip off quote characters
 | 
				
			||||||
 | 
					  offset = blockStart; // skip single character!
 | 
				
			||||||
 | 
					  blockEnd = data.find(delim, offset);
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "offset: %i blockEnd: %i ", offset, blockEnd);
 | 
				
			||||||
 | 
					  if (blockStart == std::string::npos || blockEnd == std::string::npos) {
 | 
				
			||||||
 | 
					    return false; // no start/end quotes found
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "getValueBlock: data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart)  );
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool getString(std::string &data, std::string name, std::string &output) {
 | 
				
			||||||
 | 
					  size_t blockStart = 0;
 | 
				
			||||||
 | 
					  size_t blockEnd = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!getValueBlock(data, name, blockStart, blockEnd, "\"")) {
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_FAIL, "could not find \"%s\" in data block", name.c_str());
 | 
				
			||||||
 | 
					    return false; // could not find value in this data block.
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "data.size() %i start %i end %i num %i", data.size(), blockStart,blockEnd,(blockEnd-blockStart)  )
 | 
				
			||||||
 | 
					  output = data.substr(blockStart, (blockEnd - blockStart));
 | 
				
			||||||
 | 
					  // looks like this function is working as expected
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "data in getstring %s", (data.substr(blockStart,(blockEnd-blockStart))).c_str());
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool getLong(std::string &data, std::string name, long &output) {
 | 
				
			||||||
 | 
					  size_t blockStart, blockEnd;
 | 
				
			||||||
 | 
					  if (!getValueBlock(data, name, blockStart, blockEnd, "\"")) {
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_FAIL, "could not find \"%s\" in data block", name.c_str());
 | 
				
			||||||
 | 
					    return false; // could not find value in this data block.
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "name: %s data in atol %s",name.c_str(), (data.substr(blockStart,(blockEnd-blockStart))).c_str());
 | 
				
			||||||
 | 
					  output = atol((data.substr(blockStart, (blockEnd - blockStart))).c_str());
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// block expecting separate name and /name occurence, or name and /> before another occurence of <.
 | 
				
			||||||
 | 
					bool getBlock(std::string &data, std::string name, int offset, size_t &blockStart, size_t &blockEnd) {
 | 
				
			||||||
 | 
					  blockStart = data.find("<" + name + ">", offset);
 | 
				
			||||||
 | 
					  if (blockStart == std::string::npos) {
 | 
				
			||||||
 | 
					    blockStart = data.find("<" + name + " ", offset); // this considers both valid situations <name> and <name bla="bla"/>
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (blockStart == std::string::npos) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "no block start found for name: %s at offset: %i", name.c_str(), offset);
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  blockEnd = data.find("/" + name + ">", blockStart);
 | 
				
			||||||
 | 
					  if (blockEnd == std::string::npos) {
 | 
				
			||||||
 | 
					    blockEnd = data.find("/>", blockStart);
 | 
				
			||||||
 | 
					    if (blockEnd == std::string::npos) {
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_INFO, "no block end found.");
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    size_t temp = data.find("<", blockStart + 1, (blockEnd - blockStart - 1)); // the +1 is to avoid re-interpreting the starting < //TODO!!
 | 
				
			||||||
 | 
					    if (temp != std::string::npos) {                                           // all info is epxected between <name ... />
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "block start found before block end. offset: %lu block: %s", temp, data.c_str());
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_FAIL, "special block end found");
 | 
				
			||||||
 | 
					    blockEnd += 2; // position after />
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    blockEnd += name.size() + 2; // position after /name>
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "getBlock: start: %i end: %i",blockStart,blockEnd);
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool parseAdaptationSet(std::string &data, std::set<seekPos> ¤tPos) {
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "Parsing adaptationSet: %s", data.c_str());
 | 
				
			||||||
 | 
					  size_t offset = 0;
 | 
				
			||||||
 | 
					  size_t blockStart, blockEnd;
 | 
				
			||||||
 | 
					  tempSD.trackType = OTHER;
 | 
				
			||||||
 | 
					  // get value: mimetype //todo: handle this!
 | 
				
			||||||
 | 
					  std::string mimeType;
 | 
				
			||||||
 | 
					  if (!getString(data, "mimeType", mimeType)) { // get first occurence of mimeType. --> this will break if multiple mimetypes should be read from
 | 
				
			||||||
 | 
					                                                // this block because no offset is provided. solution: use this on a substring containing the
 | 
				
			||||||
 | 
					                                                // desired information.
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "mimeType not found");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "mimeType: %s", mimeType.c_str()); // checked, OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (mimeType.find("video") != std::string::npos) { tempSD.trackType = VIDEO; }
 | 
				
			||||||
 | 
					  if (mimeType.find("audio") != std::string::npos) { tempSD.trackType = AUDIO; }
 | 
				
			||||||
 | 
					  if (tempSD.trackType == OTHER) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "no audio or video type found. giving up.");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // find an ID within this adaptationSet block.
 | 
				
			||||||
 | 
					  if (!getBlock(data, (std::string) "Representation", offset, blockStart, blockEnd)) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "Representation not found");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // representation string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::string block = data.substr(blockStart, (blockEnd - blockStart));
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "Representation block: %s", block.c_str());
 | 
				
			||||||
 | 
					  // check if block is not junk?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!getLong(block, "id", tempSD.trackID)) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "Representation id not found in block %s", block.c_str());
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "Representation/id: %li", tempSD.trackID); // checked, OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  offset = 0;
 | 
				
			||||||
 | 
					  // get values from SegmentTemplate
 | 
				
			||||||
 | 
					  if (!getBlock(data, (std::string) "SegmentTemplate", offset, blockStart, blockEnd)) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "SegmentTemplate not found");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  block = data.substr(blockStart, (blockEnd - blockStart));
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "SegmentTemplate block: %s",block.c_str());  //OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getLong(block, "timescale", tempSD.timeScale);
 | 
				
			||||||
 | 
					  getString(block, "media", tempSD.media);
 | 
				
			||||||
 | 
					  getString(block, "initialization", tempSD.initialization);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  size_t tmpBlockStart = 0;
 | 
				
			||||||
 | 
					  size_t tmpBlockEnd = 0;
 | 
				
			||||||
 | 
					  if (!getDelimBlock(tempSD.media, "RepresentationID", tmpBlockStart, tmpBlockEnd, "$")) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $RepresentationID$ in %s", tempSD.media.c_str());
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  tempSD.media.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!getDelimBlock(tempSD.media, "Time", tmpBlockStart, tmpBlockEnd, "$")) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $Time$ in %s", tempSD.media.c_str());
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  tempSD.media.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!getDelimBlock(tempSD.initialization, "RepresentationID", tmpBlockStart, tmpBlockEnd, "$")) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "Failed to find and replace $RepresentationID$ in %s", tempSD.initialization.c_str());
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  tempSD.initialization.replace(tmpBlockStart, (tmpBlockEnd - tmpBlockStart), "%d");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // get segment timeline block from within segment template:
 | 
				
			||||||
 | 
					  size_t blockOffset = 0; // offset should be 0 because this is a new block
 | 
				
			||||||
 | 
					  if (!getBlock(block, "SegmentTimeline", blockOffset, blockStart, blockEnd)) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "SegmentTimeline block not found");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::string block2 = block.substr(blockStart, (blockEnd - blockStart)); // overwrites previous block (takes just the segmentTimeline part
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "SegmentTimeline block: %s",block2.c_str()); //OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int numS = 0;
 | 
				
			||||||
 | 
					  offset = 0;
 | 
				
			||||||
 | 
					  long long unsigned int totalDuration = 0;
 | 
				
			||||||
 | 
					  long timeValue;
 | 
				
			||||||
 | 
					  while (1) {
 | 
				
			||||||
 | 
					    if (!getBlock(block2, "S", offset, blockStart, blockEnd)) {
 | 
				
			||||||
 | 
					      if (numS == 0) {
 | 
				
			||||||
 | 
					        DEBUG_MSG(DLVL_FAIL, "no S found within SegmentTimeline");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        DEBUG_MSG(DLVL_INFO, "all S found within SegmentTimeline %i", numS);
 | 
				
			||||||
 | 
					        return true; // break;  //escape from while loop (to return true)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    numS++;
 | 
				
			||||||
 | 
					    // stuff S data into: currentPos
 | 
				
			||||||
 | 
					    // searching for t(start position)
 | 
				
			||||||
 | 
					    std::string sBlock = block2.substr(blockStart, (blockEnd - blockStart));
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_INFO, "S found. offset: %i blockStart: %i blockend: %i block: %s",offset,blockStart, blockEnd, sBlock.c_str());    //OK!
 | 
				
			||||||
 | 
					    if (getLong(sBlock, "t", timeValue)) {
 | 
				
			||||||
 | 
					      totalDuration = timeValue; // reset totalDuration to value of t
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!getLong(sBlock, "d", timeValue)) { // expected duration in every S.
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "no d found within S");
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // stuff data with old value (start of block)
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_INFO, "stuffing info from S into set");
 | 
				
			||||||
 | 
					    seekPos thisPos;
 | 
				
			||||||
 | 
					    thisPos.trackType = tempSD.trackType;
 | 
				
			||||||
 | 
					    thisPos.trackID = tempSD.trackID;
 | 
				
			||||||
 | 
					    thisPos.adaptationSet = tempSD.adaptationSet;
 | 
				
			||||||
 | 
					    // thisPos.trackID=id;
 | 
				
			||||||
 | 
					    thisPos.seekTime = totalDuration; // previous total duration is start time of this S.
 | 
				
			||||||
 | 
					    thisPos.duration = timeValue;
 | 
				
			||||||
 | 
					    thisPos.timeScale = tempSD.timeScale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static char charBuf[512];
 | 
				
			||||||
 | 
					    snprintf(charBuf, 512, tempSD.media.c_str(), tempSD.trackID, totalDuration);
 | 
				
			||||||
 | 
					    thisPos.url.assign(charBuf);
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_INFO, "media url (from rep.ID %d, startTime %d): %s", tempSD.trackID, totalDuration,thisPos.url.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    currentPos.insert(thisPos); // assumes insert copies all data in seekPos struct.
 | 
				
			||||||
 | 
					    totalDuration += timeValue; // update totalDuration
 | 
				
			||||||
 | 
					    offset = blockEnd;          // blockEnd and blockStart are absolute values within string, offset is not relevant.
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool parseXML(std::string &body, std::set<seekPos> ¤tPos, std::vector<StreamData> &streamData) {
 | 
				
			||||||
 | 
					  // for all adaptation sets
 | 
				
			||||||
 | 
					  // representation ID
 | 
				
			||||||
 | 
					  int numAdaptationSet = 0;
 | 
				
			||||||
 | 
					  size_t currentOffset = 0;
 | 
				
			||||||
 | 
					  size_t adaptationSetStart;
 | 
				
			||||||
 | 
					  size_t adaptationSetEnd;
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "body received: %s", body.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (getBlock(body, "AdaptationSet", currentOffset, adaptationSetStart, adaptationSetEnd)) {
 | 
				
			||||||
 | 
					    tempSD.adaptationSet = numAdaptationSet;
 | 
				
			||||||
 | 
					    numAdaptationSet++;
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "adaptationSet found. start: %lu end: %lu num: %lu ", adaptationSetStart, adaptationSetEnd,
 | 
				
			||||||
 | 
					              (adaptationSetEnd - adaptationSetStart));
 | 
				
			||||||
 | 
					    // get substring: from <adaptationSet... to /adaptationSet>
 | 
				
			||||||
 | 
					    std::string adaptationSet = body.substr(adaptationSetStart, (adaptationSetEnd - adaptationSetStart));
 | 
				
			||||||
 | 
					    // function was verified: output as expected.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!parseAdaptationSet(adaptationSet, currentPos)) {
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "parseAdaptationSet returned false."); // this also happens in the case of OTHER mimetype. in that case it might be
 | 
				
			||||||
 | 
					                                                                  // desirable to continue searching for valid data instead of quitting.
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    streamData.push_back(tempSD);     // put temp values into adaptation set vector
 | 
				
			||||||
 | 
					    currentOffset = adaptationSetEnd; // the getblock function should make sure End is at the correct offset.
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (numAdaptationSet == 0) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "no adaptationSet found.");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "all adaptation sets found. total: %i", numAdaptationSet);
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dashAnalyser::dashAnalyser(Util::Config conf) : analysers(conf) {
 | 
				
			||||||
 | 
					  port = 80;
 | 
				
			||||||
 | 
					  url = conf.getString("url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (url.substr(0, 7) != "http://") {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "The URL must start with http://");
 | 
				
			||||||
 | 
					    // return -1;
 | 
				
			||||||
 | 
					    exit(1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  url = url.substr(7); // found problem if url is to short!! it gives out of range when entering http://meh.meh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if((url.find('/') == std::string::npos) || (url.find(".mpd") == std::string::npos))
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    std::cout << "incorrect url"<<std::endl;
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					  server = url.substr(0, url.find('/'));
 | 
				
			||||||
 | 
					  url = url.substr(url.find('/'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (server.find(':') != std::string::npos) {
 | 
				
			||||||
 | 
					    port = atoi(server.substr(server.find(':') + 1).c_str());
 | 
				
			||||||
 | 
					    server = server.substr(0, server.find(':'));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  startTime = Util::bootSecs();
 | 
				
			||||||
 | 
					  abortTime = conf.getInteger("abort");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conn = Socket::Connection(server, port, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if(!conn.connected())
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // url:
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "url %s server: %s port: %d", url.c_str(), server.c_str(), port);
 | 
				
			||||||
 | 
					  urlPrependStuff = url.substr(0, url.rfind("/") + 1);
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "prepend stuff: %s", urlPrependStuff.c_str());
 | 
				
			||||||
 | 
					  if (!conn) {
 | 
				
			||||||
 | 
					    conn = Socket::Connection(server, port, false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pos = 0;
 | 
				
			||||||
 | 
					  HTTP::Parser H;
 | 
				
			||||||
 | 
					  H.url = url;
 | 
				
			||||||
 | 
					  H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString());
 | 
				
			||||||
 | 
					  H.SendRequest(conn);
 | 
				
			||||||
 | 
					  H.Clean();
 | 
				
			||||||
 | 
					  while (conn && (!conn.spool() || !H.Read(conn))) {}
 | 
				
			||||||
 | 
					  H.BuildResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  currentPos;
 | 
				
			||||||
 | 
					  streamData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "body received: %s", H.body.c_str()); //keeps giving empty stuff :(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "url %s ", url.c_str());
 | 
				
			||||||
 | 
					  // std::ifstream in(url.c_str());
 | 
				
			||||||
 | 
					  // std::string s((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
 | 
				
			||||||
 | 
					  if (!parseXML(H.body, currentPos, streamData)) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "Manifest parsing failed. body: \n %s", H.body.c_str());
 | 
				
			||||||
 | 
					    if (conf.getString("mode") == "validate") {
 | 
				
			||||||
 | 
					      long long int endTime = Util::bootSecs();
 | 
				
			||||||
 | 
					      std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // return -1;
 | 
				
			||||||
 | 
					    exit(1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  H.Clean();
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "*********");
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "*SUMMARY*");
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "*********");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "num streams: %lu", streamData.size());
 | 
				
			||||||
 | 
					  for (unsigned int i = 0; i < streamData.size(); i++) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "");
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "ID in vector %d", i);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "trackID %ld", streamData[i].trackID);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "adaptationSet %d", streamData[i].adaptationSet);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "trackType (audio 0x02, video 0x01) %d", streamData[i].trackType);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "TimeScale %ld", streamData[i].timeScale);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "Media string %s", streamData[i].media.c_str());
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "Init string %s", streamData[i].initialization.c_str());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (unsigned int i = 0; i < streamData.size(); i++) { // get init url
 | 
				
			||||||
 | 
					    static char charBuf[512];
 | 
				
			||||||
 | 
					    snprintf(charBuf, 512, streamData[i].initialization.c_str(), streamData[i].trackID);
 | 
				
			||||||
 | 
					    streamData[i].initURL.assign(charBuf);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "init url for adaptationSet %d trackID %ld: %s ", streamData[i].adaptationSet, streamData[i].trackID,
 | 
				
			||||||
 | 
					              streamData[i].initURL.c_str());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool dashAnalyser::hasInput() {
 | 
				
			||||||
 | 
					  return currentPos.size();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool dashAnalyser::packetReady() {
 | 
				
			||||||
 | 
					  return (abortTime <= 0 || Util::bootSecs() < startTime + abortTime) && (currentPos.size() > 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dashAnalyser::~dashAnalyser() {
 | 
				
			||||||
 | 
					  INFO_MSG("stopped");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dashAnalyser::doAnalyse() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "next url: %s", currentPos.begin()->url.c_str());
 | 
				
			||||||
 | 
					  // match adaptation set and track id?
 | 
				
			||||||
 | 
					  int tempID = 0;
 | 
				
			||||||
 | 
					  for (unsigned int i = 0; i < streamData.size(); i++) {
 | 
				
			||||||
 | 
					    if (streamData[i].trackID == currentPos.begin()->trackID && streamData[i].adaptationSet == currentPos.begin()->adaptationSet) tempID = i;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!conn) { conn = Socket::Connection(server, port, false); }
 | 
				
			||||||
 | 
					  HTTP::Parser H;
 | 
				
			||||||
 | 
					  H.url = urlPrependStuff;
 | 
				
			||||||
 | 
					  H.url.append(currentPos.begin()->url);
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "Retrieving segment: %s (%llu-%llu)", H.url.c_str(), currentPos.begin()->seekTime,
 | 
				
			||||||
 | 
					            currentPos.begin()->seekTime + currentPos.begin()->duration);
 | 
				
			||||||
 | 
					  H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString()); // wut?
 | 
				
			||||||
 | 
					  H.SendRequest(conn);
 | 
				
			||||||
 | 
					  // TODO: get response?
 | 
				
			||||||
 | 
					  H.Clean();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (conn && (!conn.spool() || !H.Read(conn))) {} // ehm...
 | 
				
			||||||
 | 
					  // std::cout << "leh vomi: "<<H.body <<std::endl;
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "zut: %s", H.body.c_str());
 | 
				
			||||||
 | 
					  // strBuf[tempID].append(H.body);
 | 
				
			||||||
 | 
					  if (!H.body.size()) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "No data downloaded from %s", H.url.c_str());
 | 
				
			||||||
 | 
					    // break;
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  size_t beforeParse = H.body.size();
 | 
				
			||||||
 | 
					  MP4::Box mp4Data;
 | 
				
			||||||
 | 
					  bool mdatSeen = false;
 | 
				
			||||||
 | 
					  while (mp4Data.read(H.body)) {
 | 
				
			||||||
 | 
					    if (mp4Data.isType("mdat")) { mdatSeen = true; }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!mdatSeen) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "No mdat present. Sadface. :-(");
 | 
				
			||||||
 | 
					    // break;
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (H.body.size()) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "%lu bytes left in body. Assuming horrible things...", H.body.size()); //,H.body.c_str());
 | 
				
			||||||
 | 
					    std::cerr << H.body << std::endl;
 | 
				
			||||||
 | 
					    if (beforeParse == H.body.size()) {
 | 
				
			||||||
 | 
					      // break;
 | 
				
			||||||
 | 
					      return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  H.Clean();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pos = 1000 * (currentPos.begin()->seekTime + currentPos.begin()->duration) / streamData[tempID].timeScale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (conf.getString("mode") == "validate" && (Util::bootSecs() - startTime + 5) * 1000 < pos) {
 | 
				
			||||||
 | 
					    Util::wait(pos - (Util::bootSecs() - startTime + 5) * 1000);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  currentPos.erase(currentPos.begin());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  endTime = pos;
 | 
				
			||||||
 | 
					  return pos;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to "
 | 
				
			||||||
 | 
					                                          "do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("url", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"URL to HLS stream index file to retrieve.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("abort", JSON::fromString("{\"long\":\"abort\", \"short\":\"a\", \"arg\":\"integer\", \"default\":-1, \"help\":\"Abort after "
 | 
				
			||||||
 | 
					                                           "this many seconds of downloading. Negative values mean unlimited, which is the default.\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.addOption(
 | 
				
			||||||
 | 
					      "detail",
 | 
				
			||||||
 | 
					      JSON::fromString("{\"long\":\"detail\", \"short\":\"D\", \"arg\":\"num\", \"default\":2, \"help\":\"Detail level of analysis. \"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  conf.activate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dashAnalyser A(conf);
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main2(int argc, char **argv) {
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to "
 | 
				
			||||||
 | 
					                                          "do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("url", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"URL to HLS stream index file to retrieve.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("abort", JSON::fromString("{\"long\":\"abort\", \"short\":\"a\", \"arg\":\"integer\", \"default\":-1, \"help\":\"Abort after "
 | 
				
			||||||
 | 
					                                           "this many seconds of downloading. Negative values mean unlimited, which is the default.\"}"));
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  conf.activate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unsigned int port = 80;
 | 
				
			||||||
 | 
					  std::string url = conf.getString("url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (url.substr(0, 7) != "http://") {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "The URL must start with http://");
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  url = url.substr(7); // found problem if url is to short!! it gives out of range when entering http://meh.meh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::string server = url.substr(0, url.find('/'));
 | 
				
			||||||
 | 
					  url = url.substr(url.find('/'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (server.find(':') != std::string::npos) {
 | 
				
			||||||
 | 
					    port = atoi(server.substr(server.find(':') + 1).c_str());
 | 
				
			||||||
 | 
					    server = server.substr(0, server.find(':'));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  long long int startTime = Util::bootSecs();
 | 
				
			||||||
 | 
					  long long int abortTime = conf.getInteger("abort");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Socket::Connection conn(server, port, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // url:
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "url %s server: %s port: %d", url.c_str(), server.c_str(), port);
 | 
				
			||||||
 | 
					  std::string urlPrependStuff = url.substr(0, url.rfind("/") + 1);
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "prepend stuff: %s", urlPrependStuff.c_str());
 | 
				
			||||||
 | 
					  if (!conn) { conn = Socket::Connection(server, port, false); }
 | 
				
			||||||
 | 
					  unsigned int pos = 0;
 | 
				
			||||||
 | 
					  HTTP::Parser H;
 | 
				
			||||||
 | 
					  H.url = url;
 | 
				
			||||||
 | 
					  H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString());
 | 
				
			||||||
 | 
					  H.SendRequest(conn);
 | 
				
			||||||
 | 
					  H.Clean();
 | 
				
			||||||
 | 
					  while (conn && (!conn.spool() || !H.Read(conn))) {}
 | 
				
			||||||
 | 
					  H.BuildResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::set<seekPos> currentPos;
 | 
				
			||||||
 | 
					  std::vector<StreamData> streamData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "body received: %s", H.body.c_str()); //keeps giving empty stuff :(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "url %s ", url.c_str());
 | 
				
			||||||
 | 
					  // std::ifstream in(url.c_str());
 | 
				
			||||||
 | 
					  // std::string s((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
 | 
				
			||||||
 | 
					  if (!parseXML(H.body, currentPos, streamData)) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "Manifest parsing failed. body: \n %s", H.body.c_str());
 | 
				
			||||||
 | 
					    if (conf.getString("mode") == "validate") {
 | 
				
			||||||
 | 
					      long long int endTime = Util::bootSecs();
 | 
				
			||||||
 | 
					      std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  H.Clean();
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "*********");
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "*SUMMARY*");
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "*********");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "num streams: %lu", streamData.size());
 | 
				
			||||||
 | 
					  for (unsigned int i = 0; i < streamData.size(); i++) {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "");
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "ID in vector %d", i);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "trackID %ld", streamData[i].trackID);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "adaptationSet %d", streamData[i].adaptationSet);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "trackType (audio 0x02, video 0x01) %d", streamData[i].trackType);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "TimeScale %ld", streamData[i].timeScale);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "Media string %s", streamData[i].media.c_str());
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "Init string %s", streamData[i].initialization.c_str());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_INFO, "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (unsigned int i = 0; i < streamData.size(); i++) { // get init url
 | 
				
			||||||
 | 
					    static char charBuf[512];
 | 
				
			||||||
 | 
					    snprintf(charBuf, 512, streamData[i].initialization.c_str(), streamData[i].trackID);
 | 
				
			||||||
 | 
					    streamData[i].initURL.assign(charBuf);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "init url for adaptationSet %d trackID %ld: %s ", streamData[i].adaptationSet, streamData[i].trackID,
 | 
				
			||||||
 | 
					              streamData[i].initURL.c_str());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (currentPos.size() && (abortTime <= 0 || Util::bootSecs() < startTime + abortTime)) {
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_INFO, "next url: %s", currentPos.begin()->url.c_str());
 | 
				
			||||||
 | 
					    std::cout << "blaa" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // match adaptation set and track id?
 | 
				
			||||||
 | 
					    int tempID = 0;
 | 
				
			||||||
 | 
					    for (unsigned int i = 0; i < streamData.size(); i++) {
 | 
				
			||||||
 | 
					      if (streamData[i].trackID == currentPos.begin()->trackID && streamData[i].adaptationSet == currentPos.begin()->adaptationSet) tempID = i;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!conn) { conn = Socket::Connection(server, port, false); }
 | 
				
			||||||
 | 
					    HTTP::Parser H;
 | 
				
			||||||
 | 
					    H.url = urlPrependStuff;
 | 
				
			||||||
 | 
					    H.url.append(currentPos.begin()->url);
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_INFO, "Retrieving segment: %s (%llu-%llu)", H.url.c_str(), currentPos.begin()->seekTime,
 | 
				
			||||||
 | 
					              currentPos.begin()->seekTime + currentPos.begin()->duration);
 | 
				
			||||||
 | 
					    H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString()); // wut?
 | 
				
			||||||
 | 
					    H.SendRequest(conn);
 | 
				
			||||||
 | 
					    // TODO: get response?
 | 
				
			||||||
 | 
					    H.Clean();
 | 
				
			||||||
 | 
					    while (conn && (!conn.spool() || !H.Read(conn))) {} // ehm...
 | 
				
			||||||
 | 
					    // std::cout << "leh vomi: "<<H.body <<std::endl;
 | 
				
			||||||
 | 
					    // DEBUG_MSG(DLVL_INFO, "zut: %s", H.body.c_str());
 | 
				
			||||||
 | 
					    // strBuf[tempID].append(H.body);
 | 
				
			||||||
 | 
					    if (!H.body.size()) {
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "No data downloaded from %s", H.url.c_str());
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    size_t beforeParse = H.body.size();
 | 
				
			||||||
 | 
					    MP4::Box mp4Data;
 | 
				
			||||||
 | 
					    bool mdatSeen = false;
 | 
				
			||||||
 | 
					    while (mp4Data.read(H.body)) {
 | 
				
			||||||
 | 
					      if (mp4Data.isType("mdat")) { mdatSeen = true; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!mdatSeen) {
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "No mdat present. Sadface. :-(");
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (H.body.size()) {
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "%lu bytes left in body. Assuming horrible things...", H.body.size()); //,H.body.c_str());
 | 
				
			||||||
 | 
					      std::cerr << H.body << std::endl;
 | 
				
			||||||
 | 
					      if (beforeParse == H.body.size()) { break; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    H.Clean();
 | 
				
			||||||
 | 
					    pos = 1000 * (currentPos.begin()->seekTime + currentPos.begin()->duration) / streamData[tempID].timeScale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (conf.getString("mode") == "validate" && (Util::bootSecs() - startTime + 5) * 1000 < pos) {
 | 
				
			||||||
 | 
					      Util::wait(pos - (Util::bootSecs() - startTime + 5) * 1000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    currentPos.erase(currentPos.begin());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (conf.getString("mode") == "validate") {
 | 
				
			||||||
 | 
					    long long int endTime = Util::bootSecs();
 | 
				
			||||||
 | 
					    std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										78
									
								
								src/analysers/analyser_dash.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/analysers/analyser_dash.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,78 @@
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					#include <set>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct StreamData{
 | 
				
			||||||
 | 
					  long timeScale;
 | 
				
			||||||
 | 
					  std::string media;
 | 
				
			||||||
 | 
					  std::string initialization;
 | 
				
			||||||
 | 
					  std::string initURL;
 | 
				
			||||||
 | 
					  long trackID;        
 | 
				
			||||||
 | 
					  unsigned int adaptationSet;
 | 
				
			||||||
 | 
					  unsigned char trackType;    
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct seekPos {
 | 
				
			||||||
 | 
					  ///\brief Less-than comparison for seekPos structures.
 | 
				
			||||||
 | 
					  ///\param rhs The seekPos to compare with.
 | 
				
			||||||
 | 
					  ///\return Whether this object is smaller than rhs.
 | 
				
			||||||
 | 
					  bool operator < (const seekPos & rhs) const {
 | 
				
			||||||
 | 
					    if ((seekTime*rhs.timeScale) < (rhs.seekTime*timeScale)) {
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if ( (seekTime*rhs.timeScale) == (rhs.seekTime*timeScale)){
 | 
				
			||||||
 | 
					        if (adaptationSet < rhs.adaptationSet){
 | 
				
			||||||
 | 
					          return true;
 | 
				
			||||||
 | 
					        } else if (adaptationSet == rhs.adaptationSet){
 | 
				
			||||||
 | 
					          if (trackID < rhs.trackID) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }          
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  long timeScale;
 | 
				
			||||||
 | 
					  long long unsigned int bytePos;     /// ?
 | 
				
			||||||
 | 
					  long long unsigned int seekTime;        ///start
 | 
				
			||||||
 | 
					  long long unsigned int duration;     ///duration
 | 
				
			||||||
 | 
					  unsigned int trackID;               ///stores representation ID
 | 
				
			||||||
 | 
					  unsigned int adaptationSet;                 ///stores type
 | 
				
			||||||
 | 
					  unsigned char trackType;                 ///stores type
 | 
				
			||||||
 | 
					  std::string url;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class dashAnalyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    dashAnalyser(Util::Config config);
 | 
				
			||||||
 | 
					    ~dashAnalyser();
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					//    void doValidate();
 | 
				
			||||||
 | 
					    bool hasInput();
 | 
				
			||||||
 | 
					    void PostProcessing();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    unsigned int port;
 | 
				
			||||||
 | 
					    std::string url;
 | 
				
			||||||
 | 
					    std::string server;
 | 
				
			||||||
 | 
					    long long int startTime;
 | 
				
			||||||
 | 
					    long long int abortTime;
 | 
				
			||||||
 | 
					    Socket::Connection conn;
 | 
				
			||||||
 | 
					    std::string urlPrependStuff;
 | 
				
			||||||
 | 
					    unsigned int pos;
 | 
				
			||||||
 | 
					    std::set<seekPos> currentPos;
 | 
				
			||||||
 | 
					    std::vector<StreamData> streamData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										124
									
								
								src/analysers/analyser_dtsc.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/analysers/analyser_dtsc.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,124 @@
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "analyser_dtsc.h"
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/h264.h>
 | 
				
			||||||
 | 
					#include <mist/json.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dtscAnalyser::dtscAnalyser(Util::Config config) : analysers(config) {
 | 
				
			||||||
 | 
					  conn = Socket::Connection(fileno(stdout), fileno(stdin));
 | 
				
			||||||
 | 
					  std::cout << "connection initialized" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  F.reInit(conn);
 | 
				
			||||||
 | 
					  totalBytes = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //  F = DTSC::Packet(config.getString("filename"));
 | 
				
			||||||
 | 
					  if (!F) {
 | 
				
			||||||
 | 
					    std::cerr << "Not a valid DTSC file" << std::endl;
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (F.getVersion() == DTSC::DTSC_HEAD) // for meta
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    DTSC::Meta m(F);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (detail > 0) {
 | 
				
			||||||
 | 
					      JSON::Value result;
 | 
				
			||||||
 | 
					      for (std::map<unsigned int, DTSC::Track>::iterator it = m.tracks.begin(); it != m.tracks.end(); it++) {
 | 
				
			||||||
 | 
					        JSON::Value track;
 | 
				
			||||||
 | 
					        if (it->second.type == "video") {
 | 
				
			||||||
 | 
					          std::stringstream tStream;
 | 
				
			||||||
 | 
					          track["resolution"] = JSON::Value((long long)it->second.width).asString() + "x" + JSON::Value((long long)it->second.height).asString();
 | 
				
			||||||
 | 
					          track["fps"] = (long long)((double)it->second.fpks / 1000);
 | 
				
			||||||
 | 
					          track["fpks"] = it->second.fpks;
 | 
				
			||||||
 | 
					          tStream << it->second.bps * 8 << " b/s, " << (double)it->second.bps * 8 / 1024 << " kb/s, " << (double)it->second.bps * 8 / 1024 / 1024
 | 
				
			||||||
 | 
					                  << " mb/s";
 | 
				
			||||||
 | 
					          track["bitrate"] = tStream.str();
 | 
				
			||||||
 | 
					          tStream.str("");
 | 
				
			||||||
 | 
					          track["keyframe_duration"] = (long long)((float)(it->second.lastms - it->second.firstms) / it->second.keys.size());
 | 
				
			||||||
 | 
					          tStream << ((double)(it->second.lastms - it->second.firstms) / it->second.keys.size()) / 1000;
 | 
				
			||||||
 | 
					          track["keyframe_interval"] = tStream.str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          tStream.str("");
 | 
				
			||||||
 | 
					          if (it->second.codec == "H264") {
 | 
				
			||||||
 | 
					            h264::sequenceParameterSet sps;
 | 
				
			||||||
 | 
					            sps.fromDTSCInit(it->second.init);
 | 
				
			||||||
 | 
					            h264::SPSMeta spsData = sps.getCharacteristics();
 | 
				
			||||||
 | 
					            track["encoding"]["width"] = spsData.width;
 | 
				
			||||||
 | 
					            track["encoding"]["height"] = spsData.height;
 | 
				
			||||||
 | 
					            tStream << spsData.fps;
 | 
				
			||||||
 | 
					            track["encoding"]["fps"] = tStream.str();
 | 
				
			||||||
 | 
					            track["encoding"]["profile"] = spsData.profile;
 | 
				
			||||||
 | 
					            track["encoding"]["level"] = spsData.level;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (it->second.type == "audio") {
 | 
				
			||||||
 | 
					          std::stringstream tStream;
 | 
				
			||||||
 | 
					          tStream << it->second.bps * 8 << " b/s, " << (double)it->second.bps * 8 / 1024 << " kb/s, " << (double)it->second.bps * 8 / 1024 / 1024
 | 
				
			||||||
 | 
					                  << " mb/s";
 | 
				
			||||||
 | 
					          track["bitrate"] = tStream.str();
 | 
				
			||||||
 | 
					          track["keyframe_interval"] = (long long)((float)(it->second.lastms - it->second.firstms) / it->second.keys.size());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        result[it->second.getWritableIdentifier()] = track;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      std::cout << result.toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m.vod || m.live) { m.toPrettyString(std::cout, 0, 0x03); }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool dtscAnalyser::packetReady() {
 | 
				
			||||||
 | 
					  return (F.getDataLen() > 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool dtscAnalyser::hasInput() {
 | 
				
			||||||
 | 
					  return F;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dtscAnalyser::doAnalyse() {
 | 
				
			||||||
 | 
					  if (analyse) { // always analyse..?
 | 
				
			||||||
 | 
					    switch (F.getVersion()) {
 | 
				
			||||||
 | 
					    case DTSC::DTSC_V1: {
 | 
				
			||||||
 | 
					      std::cout << "DTSCv1 packet: " << F.getScan().toPrettyString() << std::endl;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case DTSC::DTSC_V2: {
 | 
				
			||||||
 | 
					      std::cout << "DTSCv2 packet (Track " << F.getTrackId() << ", time " << F.getTime() << "): " << F.getScan().toPrettyString() << std::endl;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case DTSC::DTSC_HEAD: {
 | 
				
			||||||
 | 
					      std::cout << "DTSC header: " << F.getScan().toPrettyString() << std::endl;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case DTSC::DTCM: {
 | 
				
			||||||
 | 
					      std::cout << "DTCM command: " << F.getScan().toPrettyString() << std::endl;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default: DEBUG_MSG(DLVL_WARN, "Invalid dtsc packet @ bpos %llu", totalBytes); break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  totalBytes += F.getDataLen();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  F.reInit(conn); 
 | 
				
			||||||
 | 
					  return totalBytes;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  analysers::defaultConfig(conf);
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dtscAnalyser A(conf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/analysers/analyser_dtsc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/analysers/analyser_dtsc.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					#include <mist/dtsc.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class dtscAnalyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  DTSC::Packet F;
 | 
				
			||||||
 | 
					  Socket::Connection conn;
 | 
				
			||||||
 | 
					  uint64_t totalBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    dtscAnalyser(Util::Config config);
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    bool hasInput();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					//    void doValidate();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/analysers/analyser_flv.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/analysers/analyser_flv.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					#include "analyser_flv.h"
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flvAnalyser::flvAnalyser(Util::Config config) : analysers(config) {
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  if(fileinput && open(filename.c_str(), O_RDONLY) <= 0)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  filter = conf.getInteger("filter");
 | 
				
			||||||
 | 
					  FLV::Tag flvData; // Temporary storage for incoming FLV data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //check for flv data
 | 
				
			||||||
 | 
					  char flvHeader[3];
 | 
				
			||||||
 | 
					  std::cin.read(flvHeader,3);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  if(flvHeader[0] != 0x46 || flvHeader[1] != 0x4C || flvHeader[2] != 0x56)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    FAIL_MSG("No FLV Signature found!");
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  std::cin.seekg(0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					void flvAnalyser::doValidate()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  std::cout << "dfasdfsdafdsf" << std::endl;
 | 
				
			||||||
 | 
					  std::cout << upTime << ", " << finTime << ", " << (finTime-upTime) << ", " << flvData.tagTime() << std::endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool flvAnalyser::hasInput() {
 | 
				
			||||||
 | 
					  return !feof(stdin);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool flvAnalyser::packetReady() {
 | 
				
			||||||
 | 
					  return flvData.FileLoader(stdin);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int flvAnalyser::doAnalyse() {
 | 
				
			||||||
 | 
					  //  std::cout<< "do analyse" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (analyse) { // always analyse..?
 | 
				
			||||||
 | 
					    if (!filter || filter == flvData.data[0]) {
 | 
				
			||||||
 | 
					      std::cout << "[" << flvData.tagTime() << "+" << flvData.offset() << "] " << flvData.tagType() << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  endTime = flvData.tagTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return endTime;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("filter", JSON::fromString("{\"arg\":\"num\", \"short\":\"f\", \"long\":\"filter\", \"default\":0, \"help\":\"Only print info "
 | 
				
			||||||
 | 
					                                            "about this tag type (8 = audio, 9 = video, 0 = all)\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  analysers::defaultConfig(conf);
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  flvAnalyser A(conf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/analysers/analyser_flv.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/analysers/analyser_flv.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					#include <mist/flv_tag.h> //FLV support
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class flvAnalyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  FLV::Tag flvData;
 | 
				
			||||||
 | 
					  long long filter;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    flvAnalyser(Util::Config config);
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					    bool hasInput();
 | 
				
			||||||
 | 
					//    void doValidate();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										221
									
								
								src/analysers/analyser_hls.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								src/analysers/analyser_hls.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,221 @@
 | 
				
			||||||
 | 
					#include "analyser_hls.h"
 | 
				
			||||||
 | 
					#include <cstdio>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					#include <mist/http_parser.h>
 | 
				
			||||||
 | 
					#include <mist/timing.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <sys/sysinfo.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// http://patchy.ddvtech.com:8080/hls/bbb/index.m3u8
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::deque<HLSPart> getParts(std::string &body, std::string &uri) {
 | 
				
			||||||
 | 
					  size_t slashPos = uri.rfind('/');
 | 
				
			||||||
 | 
					  std::string uri_prefix = uri.substr(0, slashPos + 1);
 | 
				
			||||||
 | 
					  std::deque<HLSPart> out;
 | 
				
			||||||
 | 
					  std::stringstream data(body);
 | 
				
			||||||
 | 
					  std::string line;
 | 
				
			||||||
 | 
					  unsigned int start = 0;
 | 
				
			||||||
 | 
					  unsigned int durat = 0;
 | 
				
			||||||
 | 
					  do {
 | 
				
			||||||
 | 
					    line = "";
 | 
				
			||||||
 | 
					    std::getline(data, line);
 | 
				
			||||||
 | 
					    if (line.size() && *line.rbegin() == '\r') { line.resize(line.size() - 1); }
 | 
				
			||||||
 | 
					    if (line != "") {
 | 
				
			||||||
 | 
					      if (line[0] != '#') {
 | 
				
			||||||
 | 
					        out.push_back(HLSPart(uri_prefix + line, start, durat));
 | 
				
			||||||
 | 
					        start += durat;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        if (line.substr(0, 8) == "#EXTINF:") { durat = atof(line.substr(8).c_str()) * 1000; }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } while (line != "");
 | 
				
			||||||
 | 
					  return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hlsAnalyser::hlsAnalyser(Util::Config config) : analysers(config) {
 | 
				
			||||||
 | 
					  port = 80;
 | 
				
			||||||
 | 
					  url = conf.getString("url");
 | 
				
			||||||
 | 
					  if (url.substr(0, 7) != "http://") {
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_FAIL, "The URL must start with http://");
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  url = url.substr(7);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //check if url ends with .m3u8?
 | 
				
			||||||
 | 
					  if((url.find('/') == std::string::npos) || (url.find(".m3u") == std::string::npos && url.find(".m3u8") == std::string::npos))
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    std::cout << "incorrect url"<<std::endl;
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  server = url.substr(0, url.find('/'));
 | 
				
			||||||
 | 
					  url = url.substr(url.find('/'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (server.find(':') != std::string::npos) {
 | 
				
			||||||
 | 
					    port = atoi(server.substr(server.find(':') + 1).c_str());
 | 
				
			||||||
 | 
					    server = server.substr(0, server.find(':'));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  startTime = Util::bootSecs();
 | 
				
			||||||
 | 
					  abortTime = conf.getInteger("abort");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  playlist = url;
 | 
				
			||||||
 | 
					  repeat = true; // init to true, analyse at least one time
 | 
				
			||||||
 | 
					  lastDown = "";
 | 
				
			||||||
 | 
					  pos = 0;
 | 
				
			||||||
 | 
					  output = (conf.getString("mode") == "output");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void hlsAnalyser::doValidate() {
 | 
				
			||||||
 | 
					  long long int endTime = Util::bootSecs();
 | 
				
			||||||
 | 
					  std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int hlsAnalyser::doAnalyse() {
 | 
				
			||||||
 | 
					  repeat = false;
 | 
				
			||||||
 | 
					  while (url.size() > 4 && (url.find(".m3u") != std::string::npos || url.find(".m3u8") != std::string::npos)) {
 | 
				
			||||||
 | 
					    playlist = url;
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_DEVEL, "Retrieving playlist: %s", url.c_str());
 | 
				
			||||||
 | 
					    if (!conn) { conn = Socket::Connection(server, port, false); }
 | 
				
			||||||
 | 
					    HTTP::Parser H;
 | 
				
			||||||
 | 
					    H.url = url;
 | 
				
			||||||
 | 
					    H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString());
 | 
				
			||||||
 | 
					    H.SendRequest(conn);
 | 
				
			||||||
 | 
					    H.Clean();
 | 
				
			||||||
 | 
					    while (conn && (abortTime <= 0 || Util::bootSecs() < startTime + abortTime) && (!conn.spool() || !H.Read(conn))) {}
 | 
				
			||||||
 | 
					    parts = getParts(H.body, url);
 | 
				
			||||||
 | 
					    if (!parts.size()) {
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "Playlist parsing error - cancelling. state: %s/%s body size: %u", conn ? "Conn" : "Disconn",
 | 
				
			||||||
 | 
					                (Util::bootSecs() < (startTime + abortTime)) ? "NoTimeout" : "TimedOut", H.body.size());
 | 
				
			||||||
 | 
					      if (conf.getString("mode") == "validate") {
 | 
				
			||||||
 | 
					        long long int endTime = Util::bootSecs();
 | 
				
			||||||
 | 
					        std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    H.Clean();
 | 
				
			||||||
 | 
					    url = parts.begin()->uri;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (lastDown != "") {
 | 
				
			||||||
 | 
					    while (parts.size() && parts.begin()->uri != lastDown) { parts.pop_front(); }
 | 
				
			||||||
 | 
					    if (parts.size() < 2) {
 | 
				
			||||||
 | 
					      repeat = true;
 | 
				
			||||||
 | 
					      Util::sleep(1000);
 | 
				
			||||||
 | 
					      //        continue;
 | 
				
			||||||
 | 
					      return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    parts.pop_front();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unsigned int lastRepeat = 0;
 | 
				
			||||||
 | 
					  unsigned int numRepeat = 0;
 | 
				
			||||||
 | 
					  while (parts.size() > 0 && (abortTime <= 0 || Util::bootSecs() < startTime + abortTime)) {
 | 
				
			||||||
 | 
					    HLSPart part = *parts.begin();
 | 
				
			||||||
 | 
					    parts.pop_front();
 | 
				
			||||||
 | 
					    DEBUG_MSG(DLVL_DEVEL, "Retrieving segment: %s (%u-%u)", part.uri.c_str(), part.start, part.start + part.dur);
 | 
				
			||||||
 | 
					    if (!conn) { conn = Socket::Connection(server, port, false); }
 | 
				
			||||||
 | 
					    HTTP::Parser H;
 | 
				
			||||||
 | 
					    H.url = part.uri;
 | 
				
			||||||
 | 
					    H.SetHeader("Host", server + ":" + JSON::Value((long long)port).toString());
 | 
				
			||||||
 | 
					    H.SendRequest(conn);
 | 
				
			||||||
 | 
					    H.Clean();
 | 
				
			||||||
 | 
					    while (conn && (abortTime <= 0 || Util::bootSecs() < startTime + abortTime) && (!conn.spool() || !H.Read(conn))) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (H.GetHeader("Content-Length") != "") {
 | 
				
			||||||
 | 
					      if (H.body.size() != atoi(H.GetHeader("Content-Length").c_str())) {
 | 
				
			||||||
 | 
					        DEBUG_MSG(DLVL_FAIL, "Expected %s bytes of data, but only received %lu.", H.GetHeader("Content-Length").c_str(), H.body.size());
 | 
				
			||||||
 | 
					        if (lastRepeat != part.start || numRepeat < 500) {
 | 
				
			||||||
 | 
					          DEBUG_MSG(DLVL_FAIL, "Retrying");
 | 
				
			||||||
 | 
					          if (lastRepeat != part.start) {
 | 
				
			||||||
 | 
					            numRepeat = 0;
 | 
				
			||||||
 | 
					            lastRepeat = part.start;
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            numRepeat++;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          parts.push_front(part);
 | 
				
			||||||
 | 
					          Util::wait(1000);
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          DEBUG_MSG(DLVL_FAIL, "Aborting further downloading");
 | 
				
			||||||
 | 
					          repeat = false;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (H.body.size() % 188) {
 | 
				
			||||||
 | 
					      DEBUG_MSG(DLVL_FAIL, "Expected a multiple of 188 bytes, received %d bytes", H.body.size());
 | 
				
			||||||
 | 
					      if (lastRepeat != part.start || numRepeat < 500) {
 | 
				
			||||||
 | 
					        DEBUG_MSG(DLVL_FAIL, "Retrying");
 | 
				
			||||||
 | 
					        if (lastRepeat != part.start) {
 | 
				
			||||||
 | 
					          numRepeat = 0;
 | 
				
			||||||
 | 
					          lastRepeat = part.start;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          numRepeat++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        parts.push_front(part);
 | 
				
			||||||
 | 
					        Util::wait(1000);
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        DEBUG_MSG(DLVL_FAIL, "Aborting further downloading");
 | 
				
			||||||
 | 
					        repeat = false;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pos = part.start + part.dur;
 | 
				
			||||||
 | 
					    if (conf.getString("mode") == "validate" && (Util::bootSecs() - startTime + 5) * 1000 < pos) {
 | 
				
			||||||
 | 
					      Util::wait(pos - (Util::bootSecs() - startTime + 5) * 1000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    lastDown = part.uri;
 | 
				
			||||||
 | 
					    if (output) { std::cout << H.body; }
 | 
				
			||||||
 | 
					    H.Clean();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return pos;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool hlsAnalyser::hasInput() {
 | 
				
			||||||
 | 
					  return repeat;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool hlsAnalyser::packetReady() {
 | 
				
			||||||
 | 
					  return repeat;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hlsAnalyser::~hlsAnalyser() {
 | 
				
			||||||
 | 
					  // INFO_MSG("call destructor");
 | 
				
			||||||
 | 
					  // DEBUG_MSG(DLVL_INFO, "mode: %s", conf.getString("mode").c_str());
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  /* call doValidate() from superclass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (conf.getString("mode") == "validate") {
 | 
				
			||||||
 | 
					    long long int endTime = Util::bootSecs();
 | 
				
			||||||
 | 
					    std::cout << startTime << ", " << endTime << ", " << (endTime - startTime) << ", " << pos << std::endl;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to "
 | 
				
			||||||
 | 
					                                          "do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("url", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"URL to HLS stream index file to retrieve.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("abort", JSON::fromString("{\"long\":\"abort\", \"short\":\"a\", \"arg\":\"integer\", \"default\":-1, \"help\":\"Abort after "
 | 
				
			||||||
 | 
					                                           "this many seconds of downloading. Negative values mean unlimited, which is the default.\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.addOption(
 | 
				
			||||||
 | 
					      "detail",
 | 
				
			||||||
 | 
					      JSON::fromString("{\"long\":\"detail\", \"short\":\"D\", \"arg\":\"num\", \"default\":2, \"help\":\"Detail level of analysis. \"}"));
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  conf.activate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hlsAnalyser A(conf);
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										55
									
								
								src/analysers/analyser_hls.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/analysers/analyser_hls.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					//http://cattop:8080/hls/bunny/index.m3u8 
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/timing.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					#include <mist/http_parser.h>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HLSPart {
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    HLSPart(std::string u, unsigned int s, unsigned int d) {
 | 
				
			||||||
 | 
					      uri = u;
 | 
				
			||||||
 | 
					      start = s;
 | 
				
			||||||
 | 
					      dur = d;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::string uri;
 | 
				
			||||||
 | 
					    unsigned int start;
 | 
				
			||||||
 | 
					    unsigned int dur;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class hlsAnalyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    hlsAnalyser(Util::Config config);
 | 
				
			||||||
 | 
					    ~hlsAnalyser();
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					    void doValidate();
 | 
				
			||||||
 | 
					    bool hasInput();
 | 
				
			||||||
 | 
					    void PostProcessing();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unsigned int port;
 | 
				
			||||||
 | 
					  std::string url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::string server;
 | 
				
			||||||
 | 
					  long long int startTime;
 | 
				
			||||||
 | 
					  long long int abortTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::deque<HLSPart> parts;
 | 
				
			||||||
 | 
					  Socket::Connection conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::string playlist;
 | 
				
			||||||
 | 
					  bool repeat;
 | 
				
			||||||
 | 
					  std::string lastDown;
 | 
				
			||||||
 | 
					  unsigned int pos;
 | 
				
			||||||
 | 
					  bool output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										71
									
								
								src/analysers/analyser_mp4.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/analysers/analyser_mp4.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,71 @@
 | 
				
			||||||
 | 
					#include <cstdio>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					#include <mist/mp4.h>
 | 
				
			||||||
 | 
					#include <mist/timing.h>
 | 
				
			||||||
 | 
					#include <sys/sysinfo.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "analyser_mp4.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mp4Analyser::mp4Analyser(Util::Config config) : analysers(config) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  curPos = 0;
 | 
				
			||||||
 | 
					  dataSize = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mp4Analyser::doAnalyse() {
 | 
				
			||||||
 | 
					  DEBUG_MSG(DLVL_DEVEL, "Read a box at position %d", curPos);
 | 
				
			||||||
 | 
					  std::cerr << mp4Data.toPrettyString(0) << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return dataSize; // endtime?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool mp4Analyser::hasInput() {
 | 
				
			||||||
 | 
					  if (!std::cin.good()) { return false; }
 | 
				
			||||||
 | 
					  mp4Buffer += std::cin.get();
 | 
				
			||||||
 | 
					  dataSize++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!std::cin.good()) {
 | 
				
			||||||
 | 
					    mp4Buffer.erase(mp4Buffer.size() - 1, 1);
 | 
				
			||||||
 | 
					    dataSize--;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool mp4Analyser::packetReady() {
 | 
				
			||||||
 | 
					  return mp4Data.read(mp4Buffer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mp4Analyser::~mp4Analyser() {
 | 
				
			||||||
 | 
					  INFO_MSG("Stopped parsing at position %d", curPos);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("filter", JSON::fromString("{\"arg\":\"num\", \"short\":\"f\", \"long\":\"filter\", \"default\":0, \"help\":\"Only print info "
 | 
				
			||||||
 | 
					                                            "about this tag type (8 = audio, 9 = video, 0 = all)\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to "
 | 
				
			||||||
 | 
					                                          "do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("filename",
 | 
				
			||||||
 | 
					                 JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"default\":\"\", \"help\":\"Filename of the FLV file to analyse.\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mp4Analyser A(conf);
 | 
				
			||||||
 | 
					  // FlvAnalyser A(conf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/analysers/analyser_mp4.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/analysers/analyser_mp4.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/mp4.h>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class mp4Analyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  std::string mp4Buffer;
 | 
				
			||||||
 | 
					  MP4::Box mp4Data;
 | 
				
			||||||
 | 
					  int dataSize;
 | 
				
			||||||
 | 
					  int curPos;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    mp4Analyser(Util::Config config);
 | 
				
			||||||
 | 
					    ~mp4Analyser();
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					//    void doValidate();
 | 
				
			||||||
 | 
					    bool hasInput();
 | 
				
			||||||
 | 
					    void PostProcessing();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										49
									
								
								src/analysers/analyser_ogg.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/analysers/analyser_ogg.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include "analyser_ogg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					oggAnalyser::oggAnalyser(Util::Config config) : analysers(config)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  std::cout << "ogg constr" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  filter = conf.getInteger("filter");
 | 
				
			||||||
 | 
					  FLV::Tag flvData; // Temporary storage for incoming FLV data.
 | 
				
			||||||
 | 
					  endTime = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void oggAnalyser::doValidate()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  std::cout << upTime << ", " << finTime << ", " << (finTime-upTime) << ", " << flvData.tagTime() << std::endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool oggAnalyser::packetReady()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  return flvData.FileLoader(stdin);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int oggAnalyser::doAnalyse()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (analyse){ //always analyse..?
 | 
				
			||||||
 | 
					    if (!filter || filter == flvData.data[0]){
 | 
				
			||||||
 | 
					      std::cout << "[" << flvData.tagTime() << "+" << flvData.offset() << "] " << flvData.tagType() << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  endTime = flvData.tagTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return endTime;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("filter", JSON::fromString("{\"arg\":\"num\", \"short\":\"f\", \"long\":\"filter\", \"default\":0, \"help\":\"Only print info about this tag type (8 = audio, 9 = video, 0 = all)\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("filename", JSON::fromString( "{\"arg_num\":1, \"arg\":\"string\", \"default\":\"\", \"help\":\"Filename of the FLV file to analyse.\"}"));
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  oggAnalyser A(conf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										213
									
								
								src/analysers/analyser_rtmp.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								src/analysers/analyser_rtmp.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,213 @@
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include "analyser_rtmp.h"
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <mist/flv_tag.h>
 | 
				
			||||||
 | 
					#include <mist/amf.h>
 | 
				
			||||||
 | 
					#include <mist/rtmpchunks.h>
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/socket.h>
 | 
				
			||||||
 | 
					#include <sys/sysinfo.h>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DETAIL_RECONSTRUCT 1
 | 
				
			||||||
 | 
					#define DETAIL_EXPLICIT 2
 | 
				
			||||||
 | 
					#define DETAIL_VERBOSE 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rtmpAnalyser::rtmpAnalyser(Util::Config config) : analysers(config)
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  std::cout << "rtmp constr" << std::endl; 
 | 
				
			||||||
 | 
					  Detail = conf.getInteger("detail");
 | 
				
			||||||
 | 
					  //Detail = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  inbuffer.reserve(3073);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while(std::cin.good() && inbuffer.size() < 3073){
 | 
				
			||||||
 | 
					    inbuffer += std::cin.get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  inbuffer.erase(0,3073); //strip the handshake part
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER);
 | 
				
			||||||
 | 
					  AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RTMPStream::rec_cnt += 3073;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  read_in = 0;
 | 
				
			||||||
 | 
					  endTime = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool rtmpAnalyser::packetReady()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  return (std::cin.good() || strbuf.size()); 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int rtmpAnalyser::doAnalyse()
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					//  std::cout << "do analyse" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  analyse=true;
 | 
				
			||||||
 | 
					  if (analyse){ //always analyse..?
 | 
				
			||||||
 | 
					//    std::cout << "status strbuf: " << next.Parse(strbuf) << " strbuf_size: " << strbuf.size() << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (next.Parse(strbuf)){
 | 
				
			||||||
 | 
					        if (Detail & DETAIL_VERBOSE){
 | 
				
			||||||
 | 
					          fprintf(stderr, "Chunk info: [%#2X] CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.headertype, next.cs_id, next.timestamp,
 | 
				
			||||||
 | 
					              next.len, next.msg_type_id, next.msg_stream_id);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        switch (next.msg_type_id){
 | 
				
			||||||
 | 
					          case 0: //does not exist
 | 
				
			||||||
 | 
					            fprintf(stderr, "Error chunk @ %lu - CS%i, T%i, L%i, LL%i, MID%i\n", read_in-inbuffer.size(), next.cs_id, next.timestamp, next.real_len, next.len_left, next.msg_stream_id);
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					            break; //happens when connection breaks unexpectedly
 | 
				
			||||||
 | 
					          case 1: //set chunk size
 | 
				
			||||||
 | 
					            RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str());
 | 
				
			||||||
 | 
					            fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 2: //abort message - we ignore this one
 | 
				
			||||||
 | 
					            fprintf(stderr, "CTRL: Abort message: %i\n", ntohl(*(int*)next.data.c_str()));
 | 
				
			||||||
 | 
					            //4 bytes of stream id to drop
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 3: //ack
 | 
				
			||||||
 | 
					            RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str());
 | 
				
			||||||
 | 
					            fprintf(stderr, "CTRL: Acknowledgement: %i\n", RTMPStream::snd_window_at);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 4: {
 | 
				
			||||||
 | 
					            short int ucmtype = ntohs(*(short int*)next.data.c_str());
 | 
				
			||||||
 | 
					            switch (ucmtype){
 | 
				
			||||||
 | 
					              case 0:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: stream begin %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              case 1:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: stream EOF %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              case 2:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: stream dry %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              case 3:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: setbufferlen %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              case 4:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: streamisrecorded %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              case 6:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: pingrequest %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              case 7:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: pingresponse %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              case 31:
 | 
				
			||||||
 | 
					              case 32:
 | 
				
			||||||
 | 
					                //don't know, but not interesting anyway
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              default:
 | 
				
			||||||
 | 
					                fprintf(stderr, "CTRL: User control message: UNKNOWN %hu - %u\n", ucmtype, ntohl(*(unsigned int*)(next.data.c_str()+2)));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 5: //window size of other end
 | 
				
			||||||
 | 
					            RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str());
 | 
				
			||||||
 | 
					            RTMPStream::rec_window_at = RTMPStream::rec_cnt;
 | 
				
			||||||
 | 
					            fprintf(stderr, "CTRL: Window size: %i\n", RTMPStream::rec_window_size);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 6:
 | 
				
			||||||
 | 
					            RTMPStream::snd_window_size = ntohl(*(int*)next.data.c_str());
 | 
				
			||||||
 | 
					            //4 bytes window size, 1 byte limit type (ignored)
 | 
				
			||||||
 | 
					            fprintf(stderr, "CTRL: Set peer bandwidth: %i\n", RTMPStream::snd_window_size);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 8:
 | 
				
			||||||
 | 
					          case 9:
 | 
				
			||||||
 | 
					            if (Detail & (DETAIL_EXPLICIT | DETAIL_RECONSTRUCT)){
 | 
				
			||||||
 | 
					              F.ChunkLoader(next);
 | 
				
			||||||
 | 
					              if (Detail & DETAIL_EXPLICIT){
 | 
				
			||||||
 | 
					                std::cerr << "[" << F.tagTime() << "+" << F.offset() << "] " << F.tagType() << std::endl;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              if (Detail & DETAIL_RECONSTRUCT){
 | 
				
			||||||
 | 
					                std::cout.write(F.data, F.len);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 15:
 | 
				
			||||||
 | 
					            fprintf(stderr, "Received AFM3 data message\n");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 16:
 | 
				
			||||||
 | 
					            fprintf(stderr, "Received AFM3 shared object\n");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 17: {
 | 
				
			||||||
 | 
					            fprintf(stderr, "Received AFM3 command message:\n");
 | 
				
			||||||
 | 
					            char soort = next.data[0];
 | 
				
			||||||
 | 
					            next.data = next.data.substr(1);
 | 
				
			||||||
 | 
					            if (soort == 0){
 | 
				
			||||||
 | 
					              amfdata = AMF::parse(next.data);
 | 
				
			||||||
 | 
					              std::cerr << amfdata.Print() << std::endl;
 | 
				
			||||||
 | 
					            }else{
 | 
				
			||||||
 | 
					              amf3data = AMF::parse3(next.data);
 | 
				
			||||||
 | 
					              amf3data.Print();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 18: {
 | 
				
			||||||
 | 
					            fprintf(stderr, "Received AFM0 data message (metadata):\n");
 | 
				
			||||||
 | 
					            amfdata = AMF::parse(next.data);
 | 
				
			||||||
 | 
					            amfdata.Print();
 | 
				
			||||||
 | 
					            if (Detail & DETAIL_RECONSTRUCT){
 | 
				
			||||||
 | 
					              F.ChunkLoader(next);
 | 
				
			||||||
 | 
					              std::cout.write(F.data, F.len);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 19:
 | 
				
			||||||
 | 
					            fprintf(stderr, "Received AFM0 shared object\n");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 20: { //AMF0 command message
 | 
				
			||||||
 | 
					            fprintf(stderr, "Received AFM0 command message:\n");
 | 
				
			||||||
 | 
					            amfdata = AMF::parse(next.data);
 | 
				
			||||||
 | 
					            std::cerr << amfdata.Print() << std::endl;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 22:
 | 
				
			||||||
 | 
					            if (Detail & DETAIL_RECONSTRUCT){
 | 
				
			||||||
 | 
					              std::cout << next.data;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          default:
 | 
				
			||||||
 | 
					            fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n");
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        } //switch for type of chunk
 | 
				
			||||||
 | 
					      }else{ //if chunk parsed
 | 
				
			||||||
 | 
					        if (std::cin.good()){
 | 
				
			||||||
 | 
					          unsigned int charCount = 0;
 | 
				
			||||||
 | 
					          std::string tmpbuffer;
 | 
				
			||||||
 | 
					          tmpbuffer.reserve(1024);
 | 
				
			||||||
 | 
					          while (std::cin.good() && charCount < 1024){
 | 
				
			||||||
 | 
					            char newchar = std::cin.get();
 | 
				
			||||||
 | 
					            if (std::cin.good()){
 | 
				
			||||||
 | 
					              tmpbuffer += newchar;
 | 
				
			||||||
 | 
					              ++read_in;
 | 
				
			||||||
 | 
					              ++charCount;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          strbuf.append(tmpbuffer);
 | 
				
			||||||
 | 
					        }else{
 | 
				
			||||||
 | 
					          strbuf.get().clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return endTime;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  analysers::defaultConfig(conf);
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  rtmpAnalyser A(conf);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/analysers/analyser_rtmp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/analysers/analyser_rtmp.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					#include <mist/flv_tag.h> //FLV support
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <mist/amf.h>
 | 
				
			||||||
 | 
					#include <mist/rtmpchunks.h>
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/socket.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class rtmpAnalyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  FLV::Tag flvData;
 | 
				
			||||||
 | 
					  long long filter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::string inbuffer;
 | 
				
			||||||
 | 
					  RTMPStream::Chunk next;
 | 
				
			||||||
 | 
					  FLV::Tag F; //FLV holder
 | 
				
			||||||
 | 
					  unsigned int read_in ;
 | 
				
			||||||
 | 
					  Socket::Buffer strbuf;
 | 
				
			||||||
 | 
					  AMF::Object amfdata;
 | 
				
			||||||
 | 
					  AMF::Object3 amf3data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int Detail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    rtmpAnalyser(Util::Config config);
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					//    void doValidate();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										204
									
								
								src/analysers/analyser_rtp.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								src/analysers/analyser_rtp.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,204 @@
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include "analyser_rtp.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rtpAnalyser::rtpAnalyser(Util::Config config) : analysers(config)
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  std::cout << "rtp constr" << std::endl; 
 | 
				
			||||||
 | 
					  Socket::Connection conn("localhost", 554, true);
 | 
				
			||||||
 | 
					  step = 0;
 | 
				
			||||||
 | 
					  trackIt = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void rtpAnalyser::doValidate()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool rtpAnalyser::packetReady()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  return conn.connected();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int rtpAnalyser::doAnalyse()
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  if (analyse){ //always analyse..?
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    //  std::cerr << "loopy" << std::endl;
 | 
				
			||||||
 | 
					      if(step == 0){
 | 
				
			||||||
 | 
					        HTTP_S.protocol = "RTSP/1.0";
 | 
				
			||||||
 | 
					        HTTP_S.method = "DESCRIBE";
 | 
				
			||||||
 | 
					        //rtsp://krabs:1935/vod/gear1.mp4
 | 
				
			||||||
 | 
					        //rtsp://localhost/g1
 | 
				
			||||||
 | 
					        //HTTP_S.url = "rtsp://localhost/steers";
 | 
				
			||||||
 | 
					        HTTP_S.url = "rtsp://krabs:1935/vod/steers.mp4";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HTTP_S.SetHeader("CSeq",1);
 | 
				
			||||||
 | 
					        HTTP_S.SendRequest(conn); 
 | 
				
			||||||
 | 
					        step++;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					      }else if(step == 2){
 | 
				
			||||||
 | 
					        std::cerr <<"setup " << tracks[trackIt] << std::endl;
 | 
				
			||||||
 | 
					        HTTP_S.method = "SETUP";
 | 
				
			||||||
 | 
					        HTTP_S.url = "rtsp://localhost/steers/" + tracks[trackIt];
 | 
				
			||||||
 | 
					        //HTTP_S.url = "rtsp://krabs:1935/vod/steers.mp4/" + tracks[trackIt];
 | 
				
			||||||
 | 
					        HTTP_S.SetHeader("CSeq",2+trackIt);
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        ss << "RTP/steersVP;unicast;client_port="<< 20000 + 2*trackIt<<"-"<< 20001 + 2*trackIt;
 | 
				
			||||||
 | 
					        HTTP_S.SetHeader("Transport",ss.str());//make client ports, 4200 + 2*offset
 | 
				
			||||||
 | 
					        trackIt++;
 | 
				
			||||||
 | 
					        step++;
 | 
				
			||||||
 | 
					        HTTP_S.SendRequest(conn);
 | 
				
			||||||
 | 
					        std::cerr << "step " << step << "/\\"<< ss.str()<<std::endl;
 | 
				
			||||||
 | 
					      }else if(step == 4){
 | 
				
			||||||
 | 
					        std::cerr << "Play!!!1" << std::endl;
 | 
				
			||||||
 | 
					        HTTP_S.method = "PLAY";
 | 
				
			||||||
 | 
					        HTTP_S.url = "rtsp://localhost/steers";
 | 
				
			||||||
 | 
					        //HTTP_S.url = "rtsp://krabs:1935/vod/steers.mp4";
 | 
				
			||||||
 | 
					        HTTP_S.SetHeader("Range","npt=0.000-");
 | 
				
			||||||
 | 
					        HTTP_S.SendRequest(conn);
 | 
				
			||||||
 | 
					        step++;
 | 
				
			||||||
 | 
					        std::cerr << "step for play.." << step << std::endl;
 | 
				
			||||||
 | 
					      }      
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if (conn.Received().size() || conn.spool()){ 
 | 
				
			||||||
 | 
					        if (HTTP_R.Read(conn)){
 | 
				
			||||||
 | 
					          if(step == 1){
 | 
				
			||||||
 | 
					            std::cerr << "recvd desc" << std::endl;  
 | 
				
			||||||
 | 
					            for(size_t ml = HTTP_R.body.find("a=control:",HTTP_R.body.find("m=")); ml != std::string::npos; ml = HTTP_R.body.find("a=control:",ml+1)){
 | 
				
			||||||
 | 
					              std::cerr << "found trekk" << std::endl;
 | 
				
			||||||
 | 
					              tracks.push_back(HTTP_R.body.substr(ml+10,HTTP_R.body.find_first_of("\r\n",ml)-(ml+10)));
 | 
				
			||||||
 | 
					              connections.push_back(Socket::UDPConnection());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for(unsigned int x = 0; x < connections.size();x++){
 | 
				
			||||||
 | 
					              connections[x].SetDestination("127.0.0.1",666);
 | 
				
			||||||
 | 
					              connections[x].bind(20000+2*x);
 | 
				
			||||||
 | 
					              connections[x].setBlocking(true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            step++;
 | 
				
			||||||
 | 
					          }else if(step == 3){
 | 
				
			||||||
 | 
					            std::cerr << "recvd setup" << std::endl;
 | 
				
			||||||
 | 
					            std::cerr << "trackIt: " << trackIt << " size " << tracks.size() << std::endl;
 | 
				
			||||||
 | 
					            if(trackIt < tracks.size())
 | 
				
			||||||
 | 
					              step--;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					              step++;
 | 
				
			||||||
 | 
					            std::cerr << HTTP_R.GetHeader("Transport");
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          HTTP_R.Clean();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }//!
 | 
				
			||||||
 | 
					      if(step == 5){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for(unsigned int cx = 0; cx <  connections.size(); cx++){
 | 
				
			||||||
 | 
					         // std::cerr <<"PLAY MF" << std::endl;
 | 
				
			||||||
 | 
					          if(connections[cx].Receive()){
 | 
				
			||||||
 | 
					            RTP::Packet* pakketje = new RTP::Packet(connections[cx].data, connections[cx].data_len);   
 | 
				
			||||||
 | 
					            /*std::cout << "Version = " << pakketje->getVersion() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "Padding = " << pakketje->getPadding() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "Extension = " << pakketje->getExtension() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "Contributing sources = " << pakketje->getContribCount() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "Marker = " << pakketje->getMarker() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "Payload Type = " << pakketje->getPayloadType() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "Sequence = " << pakketje->getSequence() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "Timestamp = " << pakketje->getTimeStamp() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "SSRC = " << pakketje->getSSRC() << std::endl;
 | 
				
			||||||
 | 
					            std::cout << "datalen: " << connections[cx].data_len << std::endl;  
 | 
				
			||||||
 | 
					            std::cout << "payload:" << std::endl;*/
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(pakketje->getPayloadType() == 97){
 | 
				
			||||||
 | 
					              int h264type = (int)(connections[cx].data[12] & 0x1f);
 | 
				
			||||||
 | 
					              std::cout << h264type << " - ";
 | 
				
			||||||
 | 
					              if(h264type == 0){
 | 
				
			||||||
 | 
					                std::cout << "unspecified - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 1){
 | 
				
			||||||
 | 
					                std::cout << "Coded slice of a non-IDR picture - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 2){
 | 
				
			||||||
 | 
					                std::cout << "Coded slice data partition A - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 3){
 | 
				
			||||||
 | 
					                std::cout << "Coded slice data partition B - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 4){
 | 
				
			||||||
 | 
					                std::cout << "Coded slice data partition C - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 5){
 | 
				
			||||||
 | 
					                std::cout << "Coded slice of an IDR picture - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 6){
 | 
				
			||||||
 | 
					                std::cout << "Supplemental enhancement information (SEI) - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 7){
 | 
				
			||||||
 | 
					                std::cout << "Sequence parameter set  - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 8){
 | 
				
			||||||
 | 
					                std::cout << "Picture parameter set  - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 9){
 | 
				
			||||||
 | 
					                std::cout << "Access unit delimiter  - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 10){
 | 
				
			||||||
 | 
					                std::cout << "End of sequence  - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 11){
 | 
				
			||||||
 | 
					                std::cout << "End of stream - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 12){
 | 
				
			||||||
 | 
					                std::cout << "Filler data - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 13){
 | 
				
			||||||
 | 
					                std::cout << "Sequence parameter set extension - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 14){
 | 
				
			||||||
 | 
					                std::cout << "Prefix NAL unit - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 15){
 | 
				
			||||||
 | 
					                std::cout << "Subset sequence parameter set - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 16){
 | 
				
			||||||
 | 
					                std::cout << "Reserved - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 17){
 | 
				
			||||||
 | 
					                std::cout << "Reserved - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 18){
 | 
				
			||||||
 | 
					                std::cout << "Reserved - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 19){
 | 
				
			||||||
 | 
					                std::cout << "Coded slice of an auxiliary coded picture without partitioning  - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 20){
 | 
				
			||||||
 | 
					                std::cout << "Coded slice extension - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 21){
 | 
				
			||||||
 | 
					                std::cout << "Reserved - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 22){
 | 
				
			||||||
 | 
					                std::cout << "Reserved - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 23){
 | 
				
			||||||
 | 
					                std::cout << "Reserved - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 24){
 | 
				
			||||||
 | 
					                std::cout << "stap a - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 25){
 | 
				
			||||||
 | 
					                std::cout << "stap b - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 26){
 | 
				
			||||||
 | 
					                std::cout << "mtap16 - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 27){
 | 
				
			||||||
 | 
					                std::cout << "mtap24 - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 28){
 | 
				
			||||||
 | 
					                std::cout << "fu a - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 29){
 | 
				
			||||||
 | 
					                std::cout << "fu b - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 30){
 | 
				
			||||||
 | 
					                std::cout << "Unspecified - ";
 | 
				
			||||||
 | 
					              }else if(h264type == 31){
 | 
				
			||||||
 | 
					                std::cout << "Unspecified - ";
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              
 | 
				
			||||||
 | 
					              
 | 
				
			||||||
 | 
					              
 | 
				
			||||||
 | 
					              
 | 
				
			||||||
 | 
					              for(unsigned int i = 13 ; i < connections[cx].data_len;i++){
 | 
				
			||||||
 | 
					               std::cout << std::hex <<std::setw(2) << std::setfill('0') << (int)connections[cx].data[i]<< std::dec;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            std::cout << std::endl<<std::endl;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            delete pakketje;   
 | 
				
			||||||
 | 
					          }                 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return endTime;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  rtpAnalyser A(conf);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/analysers/analyser_rtp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/analysers/analyser_rtp.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <mist/socket.h>
 | 
				
			||||||
 | 
					#include <mist/rtp.h>
 | 
				
			||||||
 | 
					#include <mist/http_parser.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class rtpAnalyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  Socket::Connection conn;
 | 
				
			||||||
 | 
					  HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.
 | 
				
			||||||
 | 
					  int step;
 | 
				
			||||||
 | 
					  std::vector<std::string> tracks;
 | 
				
			||||||
 | 
					  std::vector<Socket::UDPConnection> connections;
 | 
				
			||||||
 | 
					  unsigned int trackIt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    rtpAnalyser(Util::Config config);
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					    void doValidate();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										211
									
								
								src/analysers/analyser_ts.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/analysers/analyser_ts.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,211 @@
 | 
				
			||||||
 | 
					#include "analyser_ts.h"
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					#include <cstdio>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <mist/bitfields.h>
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/ts_packet.h>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tsAnalyser::tsAnalyser(Util::Config config) : analysers(config) {
 | 
				
			||||||
 | 
					  upTime = Util::bootSecs();
 | 
				
			||||||
 | 
					  pcr = 0;
 | 
				
			||||||
 | 
					  bytes = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  detailLevel = detail;
 | 
				
			||||||
 | 
					  endTime = 0;
 | 
				
			||||||
 | 
					  incorrectPacket = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void tsAnalyser::doValidate() {
 | 
				
			||||||
 | 
					  long long int finTime = Util::bootSecs();
 | 
				
			||||||
 | 
					  fprintf(stdout, "time since boot,time at completion,real time duration of data receival,video duration\n");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%lli000,%lli000,%lli000,%li \n", upTime, finTime, finTime - upTime, pcr / 27000);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool tsAnalyser::packetReady() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if(incorrectPacket > 5)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    mayExecute = false;
 | 
				
			||||||
 | 
					    //FAIL_MSG("too many incorrect ts packets!");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return std::cin.good();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int tsAnalyser::doAnalyse() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  char tsIdentifier = std::cin.peek();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  if(tsIdentifier != 0x47)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    incorrectPacket++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cin.read(packetPtr, 188);
 | 
				
			||||||
 | 
					//0x47
 | 
				
			||||||
 | 
					  if (std::cin.gcount() != 188) { return 0; }
 | 
				
			||||||
 | 
					  bytes += 188;
 | 
				
			||||||
 | 
					  if (packet.FromPointer(packetPtr)) {
 | 
				
			||||||
 | 
					    if (analyse) {
 | 
				
			||||||
 | 
					      if (packet.getUnitStart() && payloads[packet.getPID()] != "") {
 | 
				
			||||||
 | 
					        std::cout << printPES(payloads[packet.getPID()], packet.getPID(), detailLevel);
 | 
				
			||||||
 | 
					        payloads.erase(packet.getPID());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (detailLevel >= 3 || !packet.getPID() || packet.isPMT()) {
 | 
				
			||||||
 | 
					        if (packet.getPID() == 0) { ((TS::ProgramAssociationTable *)&packet)->parsePIDs(); }
 | 
				
			||||||
 | 
					        std::cout << packet.toPrettyString(0, detailLevel);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (packet.getPID() && !packet.isPMT() && (payloads[packet.getPID()].size() || packet.getUnitStart())) {
 | 
				
			||||||
 | 
					        payloads[packet.getPID()].append(packet.getPayload(), packet.getPayloadLength());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (packet && packet.getAdaptationField() > 1 && packet.hasPCR()) { pcr = packet.getPCR(); }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (bytes > 1024) {
 | 
				
			||||||
 | 
					    long long int tTime = Util::bootSecs();
 | 
				
			||||||
 | 
					    if (validate && tTime - upTime > 5 && tTime - upTime > pcr / 27000000) {
 | 
				
			||||||
 | 
					      std::cerr << "data received too slowly" << std::endl;
 | 
				
			||||||
 | 
					      return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bytes = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return endTime;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  analysers::defaultConfig(conf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.addOption("filter", JSON::fromString("{\"arg\":\"num\", \"short\":\"f\", \"long\":\"filter\", \"default\":0, \"help\":\"Only print info "
 | 
				
			||||||
 | 
					                                            "about this tag type (8 = audio, 9 = video, 0 = all)\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // override default detail level with specific detail level for TS Analyser.
 | 
				
			||||||
 | 
					  conf.addOption("detail", JSON::fromString("{\"long\":\"detail\", \"short\":\"D\", \"arg\":\"num\", \"default\":2, \"help\":\"Detail level of "
 | 
				
			||||||
 | 
					                                            "analysis. 1 = PES only, 2 = PAT/PMT (default), 3 = all TS packets, 9 = raw PES packet bytes, 10 = "
 | 
				
			||||||
 | 
					                                            "raw TS packet bytes\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  tsAnalyser A(conf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  A.Run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tsAnalyser::~tsAnalyser() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (std::map<unsigned long long, std::string>::iterator it = payloads.begin(); it != payloads.end(); it++) {
 | 
				
			||||||
 | 
					    if (!it->first || it->first == 4096) { continue; }
 | 
				
			||||||
 | 
					    std::cout << printPES(it->second, it->first, detailLevel);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string tsAnalyser::printPES(const std::string &d, unsigned long PID, int detailLevel) {
 | 
				
			||||||
 | 
					  unsigned int headSize = 0;
 | 
				
			||||||
 | 
					  std::stringstream res;
 | 
				
			||||||
 | 
					  bool known = false;
 | 
				
			||||||
 | 
					  res << "[PES " << PID << "]";
 | 
				
			||||||
 | 
					  if ((d[3] & 0xF0) == 0xE0) {
 | 
				
			||||||
 | 
					    res << " [Video " << (int)(d[3] & 0xF) << "]";
 | 
				
			||||||
 | 
					    known = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!known && (d[3] & 0xE0) == 0xC0) {
 | 
				
			||||||
 | 
					    res << " [Audio " << (int)(d[3] & 0x1F) << "]";
 | 
				
			||||||
 | 
					    known = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!known) { res << " [Unknown stream ID: " << (int)d[3] << "]"; }
 | 
				
			||||||
 | 
					  if (d[0] != 0 || d[1] != 0 || d[2] != 1) { res << " [!!! INVALID START CODE: " << (int)d[0] << " " << (int)d[1] << " " << (int)d[2] << " ]"; }
 | 
				
			||||||
 | 
					  unsigned int padding = 0;
 | 
				
			||||||
 | 
					  if (known) {
 | 
				
			||||||
 | 
					    if ((d[6] & 0xC0) != 0x80) { res << " [!INVALID FIRST BITS!]"; }
 | 
				
			||||||
 | 
					    if (d[6] & 0x30) { res << " [SCRAMBLED]"; }
 | 
				
			||||||
 | 
					    if (d[6] & 0x08) { res << " [Priority]"; }
 | 
				
			||||||
 | 
					    if (d[6] & 0x04) { res << " [Aligned]"; }
 | 
				
			||||||
 | 
					    if (d[6] & 0x02) { res << " [Copyrighted]"; }
 | 
				
			||||||
 | 
					    if (d[6] & 0x01) {
 | 
				
			||||||
 | 
					      res << " [Original]";
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      res << " [Copy]";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int timeFlags = ((d[7] & 0xC0) >> 6);
 | 
				
			||||||
 | 
					    if (timeFlags == 2) { headSize += 5; }
 | 
				
			||||||
 | 
					    if (timeFlags == 3) { headSize += 10; }
 | 
				
			||||||
 | 
					    if (d[7] & 0x20) {
 | 
				
			||||||
 | 
					      res << " [ESCR present, not decoded!]";
 | 
				
			||||||
 | 
					      headSize += 6;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (d[7] & 0x10) {
 | 
				
			||||||
 | 
					      uint32_t es_rate = (Bit::btoh24(d.data() + 9 + headSize) & 0x7FFFFF) >> 1;
 | 
				
			||||||
 | 
					      res << " [ESR: " << (es_rate * 50) / 1024 << " KiB/s]";
 | 
				
			||||||
 | 
					      headSize += 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (d[7] & 0x08) {
 | 
				
			||||||
 | 
					      res << " [Trick mode present, not decoded!]";
 | 
				
			||||||
 | 
					      headSize += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (d[7] & 0x04) {
 | 
				
			||||||
 | 
					      res << " [Add. copy present, not decoded!]";
 | 
				
			||||||
 | 
					      headSize += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (d[7] & 0x02) {
 | 
				
			||||||
 | 
					      res << " [CRC present, not decoded!]";
 | 
				
			||||||
 | 
					      headSize += 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (d[7] & 0x01) {
 | 
				
			||||||
 | 
					      res << " [Extension present, not decoded!]";
 | 
				
			||||||
 | 
					      headSize += 0; /// \todo Implement this. Complicated field, bah.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (d[8] != headSize) {
 | 
				
			||||||
 | 
					      padding = d[8] - headSize;
 | 
				
			||||||
 | 
					      res << " [Padding: " << padding << "b]";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (timeFlags & 0x02) {
 | 
				
			||||||
 | 
					      long long unsigned int time = (((unsigned int)d[9] & 0xE) >> 1);
 | 
				
			||||||
 | 
					      time <<= 15;
 | 
				
			||||||
 | 
					      time |= ((unsigned int)d[10] << 7) | (((unsigned int)d[11] >> 1) & 0x7F);
 | 
				
			||||||
 | 
					      time <<= 15;
 | 
				
			||||||
 | 
					      time |= ((unsigned int)d[12] << 7) | (((unsigned int)d[13] >> 1) & 0x7F);
 | 
				
			||||||
 | 
					      res << " [PTS " << ((double)time / 90000) << "s]";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (timeFlags & 0x01) {
 | 
				
			||||||
 | 
					      long long unsigned int time = ((d[14] >> 1) & 0x07);
 | 
				
			||||||
 | 
					      time <<= 15;
 | 
				
			||||||
 | 
					      time |= ((int)d[15] << 7) | (d[16] >> 1);
 | 
				
			||||||
 | 
					      time <<= 15;
 | 
				
			||||||
 | 
					      time |= ((int)d[17] << 7) | (d[18] >> 1);
 | 
				
			||||||
 | 
					      res << " [DTS " << ((double)time / 90000) << "s]";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if ((((int)d[4]) << 8 | d[5]) != (d.size() - 6)) { res << " [Size " << (((int)d[4]) << 8 | d[5]) << " => " << (d.size() - 6) << "]"; }
 | 
				
			||||||
 | 
					  res << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (detailLevel == 10) {
 | 
				
			||||||
 | 
					    unsigned int counter = 0;
 | 
				
			||||||
 | 
					    for (unsigned int i = 9 + headSize + padding; i < d.size(); ++i) {
 | 
				
			||||||
 | 
					      if ((i < d.size() - 4) && d[i] == 0 && d[i + 1] == 0 && d[i + 2] == 0 && d[i + 3] == 1) {
 | 
				
			||||||
 | 
					        res << std::endl;
 | 
				
			||||||
 | 
					        counter = 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      res << std::hex << std::setw(2) << std::setfill('0') << (int)(d[i] & 0xff) << " ";
 | 
				
			||||||
 | 
					      counter++;
 | 
				
			||||||
 | 
					      if ((counter) % 32 == 31) { res << std::endl; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res << std::endl;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return res.str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/analysers/analyser_ts.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/analysers/analyser_ts.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					#include <mist/config.h>
 | 
				
			||||||
 | 
					#include <mist/ts_packet.h>
 | 
				
			||||||
 | 
					#include "analyser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class tsAnalyser : public analysers 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  long long filter;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  std::map<unsigned long long, std::string> payloads;
 | 
				
			||||||
 | 
					  TS::Packet packet;
 | 
				
			||||||
 | 
					  long long int upTime;
 | 
				
			||||||
 | 
					  int64_t pcr;
 | 
				
			||||||
 | 
					  unsigned int bytes;
 | 
				
			||||||
 | 
					  char packetPtr[188];
 | 
				
			||||||
 | 
					  int detailLevel;
 | 
				
			||||||
 | 
					  int incorrectPacket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    tsAnalyser(Util::Config config);
 | 
				
			||||||
 | 
					    ~tsAnalyser();
 | 
				
			||||||
 | 
					    bool packetReady();
 | 
				
			||||||
 | 
					    void PreProcessing();
 | 
				
			||||||
 | 
					    //int Analyse();
 | 
				
			||||||
 | 
					    int doAnalyse();
 | 
				
			||||||
 | 
					    void doValidate();
 | 
				
			||||||
 | 
					    std::string printPES(const std::string & d, unsigned long PID, int detailLevel);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -15,22 +15,35 @@
 | 
				
			||||||
#include <mist/config.h>
 | 
					#include <mist/config.h>
 | 
				
			||||||
#include <mist/timing.h>
 | 
					#include <mist/timing.h>
 | 
				
			||||||
#include <sys/sysinfo.h>
 | 
					#include <sys/sysinfo.h>
 | 
				
			||||||
 | 
					#include <mist/defines.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///Debugging tool for FLV data.
 | 
					
 | 
				
			||||||
/// Expects FLV data through stdin, outputs human-readable information to stderr.
 | 
					namespace Analysers {
 | 
				
			||||||
int main(int argc, char ** argv){
 | 
					
 | 
				
			||||||
  Util::Config conf = Util::Config(argv[0]);
 | 
					  int analyseFLV(Util::Config conf){
 | 
				
			||||||
  conf.addOption("filter", JSON::fromString("{\"arg\":\"num\", \"short\":\"f\", \"long\":\"filter\", \"default\":0, \"help\":\"Only print info about this tag type (8 = audio, 9 = video, 0 = all)\"}"));
 | 
					    bool fileinput = conf.getString("filename").length() > 0;
 | 
				
			||||||
  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
					 | 
				
			||||||
  conf.parseArgs(argc, argv);
 | 
					 | 
				
			||||||
    bool analyse = conf.getString("mode") == "analyse";
 | 
					    bool analyse = conf.getString("mode") == "analyse";
 | 
				
			||||||
    bool validate = conf.getString("mode") == "validate";
 | 
					    bool validate = conf.getString("mode") == "validate";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    long long filter = conf.getInteger("filter");
 | 
					    long long filter = conf.getInteger("filter");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(fileinput){
 | 
				
			||||||
 | 
					      std::string filename = conf.getString("filename");
 | 
				
			||||||
 | 
					      int fp = open(filename.c_str(), O_RDONLY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if(fp <= 0){
 | 
				
			||||||
 | 
					        FAIL_MSG("Cannot open file: %s",filename.c_str());
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      dup2(fp, STDIN_FILENO);
 | 
				
			||||||
 | 
					      close(fp);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  FLV::Tag flvData; // Temporary storage for incoming FLV data.
 | 
						  FLV::Tag flvData; // Temporary storage for incoming FLV data.
 | 
				
			||||||
    long long int endTime = 0;
 | 
					    long long int endTime = 0;
 | 
				
			||||||
    long long int upTime = Util::bootSecs();
 | 
					    long long int upTime = Util::bootSecs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while(!feof(stdin)){
 | 
					    while(!feof(stdin)){
 | 
				
			||||||
      if (flvData.FileLoader(stdin)){
 | 
					      if (flvData.FileLoader(stdin)){
 | 
				
			||||||
        if (analyse){
 | 
					        if (analyse){
 | 
				
			||||||
| 
						 | 
					@ -41,9 +54,26 @@ int main(int argc, char ** argv){
 | 
				
			||||||
        endTime = flvData.tagTime();
 | 
					        endTime = flvData.tagTime();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    long long int finTime = Util::bootSecs();
 | 
					    long long int finTime = Util::bootSecs();
 | 
				
			||||||
    if (validate){
 | 
					    if (validate){
 | 
				
			||||||
      std::cout << upTime << ", " << finTime << ", " << (finTime-upTime) << ", " << endTime << std::endl;
 | 
					      std::cout << upTime << ", " << finTime << ", " << (finTime-upTime) << ", " << endTime << std::endl;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					///Debugging tool for FLV data.
 | 
				
			||||||
 | 
					/// Expects FLV data through stdin, outputs human-readable information to stderr.
 | 
				
			||||||
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("filter", JSON::fromString("{\"arg\":\"num\", \"short\":\"f\", \"long\":\"filter\", \"default\":0, \"help\":\"Only print info about this tag type (8 = audio, 9 = video, 0 = all)\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("mode", JSON::fromString("{\"long\":\"mode\", \"arg\":\"string\", \"short\":\"m\", \"default\":\"analyse\", \"help\":\"What to do with the stream. Valid modes are 'analyse', 'validate', 'output'.\"}"));
 | 
				
			||||||
 | 
					  conf.addOption("filename", JSON::fromString( "{\"arg_num\":1, \"arg\":\"string\", \"default\":\"\", \"help\":\"Filename of the FLV file to analyse.\"}")); 
 | 
				
			||||||
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return Analysers::analyseFLV(conf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,42 @@ namespace Analysers {
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Expects MP4 data through stdin, outputs human-readable information to stderr.
 | 
					  /// Expects MP4 data through stdin, outputs human-readable information to stderr.
 | 
				
			||||||
  ///\return The return code of the analyser.
 | 
					  ///\return The return code of the analyser.
 | 
				
			||||||
  int analyseMP4(){
 | 
					  int analyseMP4(Util::Config conf){
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    std::string filename = conf.getString("filename");      
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if(filename.length() > 0){
 | 
				
			||||||
 | 
					      int fp = open(filename.c_str(), O_RDONLY);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					      if(fp <= 0){
 | 
				
			||||||
 | 
					        FAIL_MSG("Cannot open file: %s",filename.c_str());
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      dup2(fp, STDIN_FILENO);
 | 
				
			||||||
 | 
					      close(fp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    MP4::Box mp4Data;
 | 
				
			||||||
 | 
					    int dataSize = 0;//mp4Buffer.size();
 | 
				
			||||||
 | 
					    int curPos = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while (!feof(stdin)){
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if(mp4Data.read(stdin))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        DEBUG_MSG(DLVL_DEVEL, "Read a box at position %d", curPos);
 | 
				
			||||||
 | 
					        std::cerr << mp4Data.toPrettyString(0) << std::endl;
 | 
				
			||||||
 | 
					        //curPos += dataSize - mp4Buffer.size();
 | 
				
			||||||
 | 
					        //dataSize = mp4Buffer.size();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    std::string mp4Buffer;
 | 
					    std::string mp4Buffer;
 | 
				
			||||||
    //Read all of std::cin to mp4Buffer
 | 
					    //Read all of std::cin to mp4Buffer
 | 
				
			||||||
    while (std::cin.good()){
 | 
					    while (std::cin.good()){
 | 
				
			||||||
| 
						 | 
					@ -25,15 +60,19 @@ namespace Analysers {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    mp4Buffer.erase(mp4Buffer.size() - 1, 1);
 | 
					    mp4Buffer.erase(mp4Buffer.size() - 1, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    MP4::Box mp4Data;
 | 
					    MP4::Box mp4Data;
 | 
				
			||||||
    int dataSize = mp4Buffer.size();
 | 
					    int dataSize = mp4Buffer.size();
 | 
				
			||||||
    int curPos = 0;
 | 
					    int curPos = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (mp4Data.read(mp4Buffer)){
 | 
					    while (mp4Data.read(mp4Buffer)){
 | 
				
			||||||
      DEBUG_MSG(DLVL_DEVEL, "Read a box at position %d", curPos);
 | 
					      DEBUG_MSG(DLVL_DEVEL, "Read a box at position %d", curPos);
 | 
				
			||||||
      std::cerr << mp4Data.toPrettyString(0) << std::endl;
 | 
					      std::cerr << mp4Data.toPrettyString(0) << std::endl;
 | 
				
			||||||
      curPos += dataSize - mp4Buffer.size();
 | 
					      curPos += dataSize - mp4Buffer.size();
 | 
				
			||||||
      dataSize = mp4Buffer.size();
 | 
					      dataSize = mp4Buffer.size();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    DEBUG_MSG(DLVL_DEVEL, "Stopped parsing at position %d", curPos);
 | 
					    DEBUG_MSG(DLVL_DEVEL, "Stopped parsing at position %d", curPos);
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -43,7 +82,8 @@ namespace Analysers {
 | 
				
			||||||
/// Expects MP4 data through stdin, outputs human-readable information to stderr.
 | 
					/// Expects MP4 data through stdin, outputs human-readable information to stderr.
 | 
				
			||||||
int main(int argc, char ** argv){
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
  Util::Config conf = Util::Config(argv[0]);
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
 | 
					  conf.addOption("filename", JSON::fromString( "{\"arg_num\":1, \"arg\":\"string\", \"default\":\"\", \"help\":\"Filename of the MP4 file to analyse.\"}"));
 | 
				
			||||||
  conf.parseArgs(argc, argv);
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
  return Analysers::analyseMP4();
 | 
					  return Analysers::analyseMP4(conf);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -118,6 +118,167 @@ namespace Analysers{
 | 
				
			||||||
    return r.str();
 | 
					    return r.str();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int newfunc(Util::Config & conf){
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    std::map<int,std::string> sn2Codec;
 | 
				
			||||||
 | 
					    std::string oggBuffer;
 | 
				
			||||||
 | 
					    OGG::Page oggPage;
 | 
				
			||||||
 | 
					    int kfgshift;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    //validate variables
 | 
				
			||||||
 | 
					    bool doValidate = true;
 | 
				
			||||||
 | 
					    long long int lastTime =0;
 | 
				
			||||||
 | 
					    double mspft = 0;
 | 
				
			||||||
 | 
					    std::map<long long unsigned int,long long int> oggMap;
 | 
				
			||||||
 | 
					    theora::header * theader = 0;
 | 
				
			||||||
 | 
					    bool seenIDheader = false;
 | 
				
			||||||
 | 
					    struct sysinfo sinfo;
 | 
				
			||||||
 | 
					    sysinfo(&sinfo);
 | 
				
			||||||
 | 
					    long long int upTime = sinfo.uptime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (oggPage.read(stdin)){ //reading ogg to string
 | 
				
			||||||
 | 
					      //print the Ogg page details, if requested
 | 
				
			||||||
 | 
					        if (conf.getBool("pages")){
 | 
				
			||||||
 | 
					          printf("%s", oggPage.toPrettyString().c_str());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //attempt to detect codec if this is the first page of a stream
 | 
				
			||||||
 | 
					          if (oggPage.getHeaderType() & OGG::BeginOfStream){
 | 
				
			||||||
 | 
					            if (memcmp("theora", oggPage.getSegment(0) + 1, 6) == 0){
 | 
				
			||||||
 | 
					              sn2Codec[oggPage.getBitstreamSerialNumber()] = "Theora";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              if(doValidate){  
 | 
				
			||||||
 | 
					                if(!seenIDheader){
 | 
				
			||||||
 | 
					                  if (theader){delete theader; theader = 0;}
 | 
				
			||||||
 | 
					                  theader = new theora::header((char*)oggPage.getSegment(0),oggPage.getSegmentLen(0));
 | 
				
			||||||
 | 
					                  if(theader->getHeaderType() == 0){
 | 
				
			||||||
 | 
					                    seenIDheader = true;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  mspft = (double)(theader->getFRD() * 1000) / theader->getFRN();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(oggPage.getGranulePosition() != 0xffffffffffffffff){
 | 
				
			||||||
 | 
					                  lastTime = ((oggPage.getGranulePosition()>>(int)theader->getKFGShift())*mspft);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (memcmp("vorbis", oggPage.getSegment(0) + 1, 6) == 0){
 | 
				
			||||||
 | 
					              sn2Codec[oggPage.getBitstreamSerialNumber()] = "Vorbis";
 | 
				
			||||||
 | 
					              
 | 
				
			||||||
 | 
					              if(oggMap.find(oggPage.getBitstreamSerialNumber()) == oggMap.end()){
 | 
				
			||||||
 | 
					              //validate stuff
 | 
				
			||||||
 | 
					                if(doValidate){
 | 
				
			||||||
 | 
					                vorbis::header vheader((char*)oggPage.getSegment(0),oggPage.getSegmentLen(0));
 | 
				
			||||||
 | 
					                  //if (vheader){
 | 
				
			||||||
 | 
					                    oggMap[oggPage.getBitstreamSerialNumber()] = ntohl(vheader.getAudioSampleRate());
 | 
				
			||||||
 | 
					                    //oggPage.setInternalCodec(sn2Codec[oggPage.getBitstreamSerialNumber()]);
 | 
				
			||||||
 | 
					                  //}
 | 
				
			||||||
 | 
					                  lastTime = (double)oggPage.getGranulePosition()/(double)oggMap[oggPage.getBitstreamSerialNumber()];
 | 
				
			||||||
 | 
					                } 
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if(doValidate){    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fprintf(stdout,"printing timing....");
 | 
				
			||||||
 | 
					            if (theader){delete theader; theader = 0;}
 | 
				
			||||||
 | 
					            sysinfo(&sinfo);
 | 
				
			||||||
 | 
					            long long int finTime = sinfo.uptime;
 | 
				
			||||||
 | 
					            fprintf(stdout,"time since boot,time at completion,real time duration of data receival,video duration\n");
 | 
				
			||||||
 | 
					            fprintf(stdout, "%lli000,%lli000,%lli000,%lli \n",upTime,finTime,finTime-upTime,lastTime);
 | 
				
			||||||
 | 
					            //print last time
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (memcmp("OpusHead", oggPage.getSegment(0), 8) == 0){
 | 
				
			||||||
 | 
					              sn2Codec[oggPage.getBitstreamSerialNumber()] = "Opus";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (sn2Codec[oggPage.getBitstreamSerialNumber()] != ""){
 | 
				
			||||||
 | 
					              std::cout << "Bitstream " << oggPage.getBitstreamSerialNumber() << " recognized as " << sn2Codec[oggPage.getBitstreamSerialNumber()] << std::endl;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              std::cout << "Bitstream " << oggPage.getBitstreamSerialNumber() << " could not be recognized as any known codec" << std::endl;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Theora"){
 | 
				
			||||||
 | 
					        std::cout << "  Theora data" << std::endl;
 | 
				
			||||||
 | 
					        static unsigned int numParts = 0;
 | 
				
			||||||
 | 
					        static unsigned int keyCount = 0;
 | 
				
			||||||
 | 
					        for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
 | 
				
			||||||
 | 
					          theora::header tmpHeader((char *)oggPage.getSegment(i), oggPage.getAllSegments()[i].size());
 | 
				
			||||||
 | 
					          if (tmpHeader.isHeader()){
 | 
				
			||||||
 | 
					            if (tmpHeader.getHeaderType() == 0){
 | 
				
			||||||
 | 
					              kfgshift = tmpHeader.getKFGShift();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            if (!(oggPage.getHeaderType() == OGG::Continued) && tmpHeader.getFTYPE() == 0){ //if keyframe
 | 
				
			||||||
 | 
					              std::cout << "keyframe granule: " << (oggPage.getGranulePosition()  >> kfgshift) << std::endl;
 | 
				
			||||||
 | 
					              std::cout << "keyframe " << keyCount << " has " << numParts << " parts, yo." << std::endl;
 | 
				
			||||||
 | 
					              numParts = 0;
 | 
				
			||||||
 | 
					              keyCount++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (oggPage.getHeaderType() != OGG::Continued || i){
 | 
				
			||||||
 | 
					              numParts++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          std::cout << tmpHeader.toPrettyString(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Vorbis"){
 | 
				
			||||||
 | 
					        std::cout << "  Vorbis data" << std::endl;
 | 
				
			||||||
 | 
					        for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){          
 | 
				
			||||||
 | 
					          int len = oggPage.getAllSegments()[i].size();
 | 
				
			||||||
 | 
					          vorbis::header tmpHeader((char*)oggPage.getSegment(i), len);
 | 
				
			||||||
 | 
					          if (tmpHeader.isHeader()){
 | 
				
			||||||
 | 
					            std::cout << tmpHeader.toPrettyString(4);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Opus"){
 | 
				
			||||||
 | 
					        std::cout << "  Opus data" << std::endl;
 | 
				
			||||||
 | 
					        int offset = 0;
 | 
				
			||||||
 | 
					        for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
 | 
				
			||||||
 | 
					          int len = oggPage.getAllSegments()[i].size();
 | 
				
			||||||
 | 
					          const char * part = oggPage.getSegment(i);
 | 
				
			||||||
 | 
					          if (len >= 8 && memcmp(part, "Opus", 4) == 0){
 | 
				
			||||||
 | 
					            if (memcmp(part, "OpusHead", 8) == 0){
 | 
				
			||||||
 | 
					              std::cout << "  Version: " << (int)(part[8]) << std::endl;
 | 
				
			||||||
 | 
					              std::cout << "  Channels: " << (int)(part[9]) << std::endl;
 | 
				
			||||||
 | 
					              std::cout << "  Pre-skip: " << (int)(part[10] + (part[11] << 8)) << std::endl;
 | 
				
			||||||
 | 
					              std::cout << "  Orig. sample rate: " << (int)(part[12] + (part[13] << 8) + (part[14] << 16) + (part[15] << 24)) << std::endl;
 | 
				
			||||||
 | 
					              std::cout << "  Gain: " << (int)(part[16] + (part[17] << 8)) << std::endl;
 | 
				
			||||||
 | 
					              std::cout << "  Channel map: " << (int)(part[18]) << std::endl;
 | 
				
			||||||
 | 
					              if (part[18] > 0){
 | 
				
			||||||
 | 
					                std::cout << "  Channel map family " << (int)(part[18]) << " not implemented - output incomplete" << std::endl;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (memcmp(part, "OpusTags", 8) == 0){
 | 
				
			||||||
 | 
					              unsigned int vendor_len = part[8] + (part[9] << 8) + (part[10] << 16) + (part[11] << 24);
 | 
				
			||||||
 | 
					              std::cout << "  Vendor: " << std::string(part + 12, vendor_len) << std::endl;
 | 
				
			||||||
 | 
					              const char * str_data = part + 12 + vendor_len;
 | 
				
			||||||
 | 
					              unsigned int strings = str_data[0] + (str_data[1] << 8) + (str_data[2] << 16) + (str_data[3] << 24);
 | 
				
			||||||
 | 
					              std::cout << "  Tags: (" << strings << ")" << std::endl;
 | 
				
			||||||
 | 
					              str_data += 4;
 | 
				
			||||||
 | 
					              for (unsigned int j = 0; j < strings; j++){
 | 
				
			||||||
 | 
					                unsigned int strlen = str_data[0] + (str_data[1] << 8) + (str_data[2] << 16) + (str_data[3] << 24);
 | 
				
			||||||
 | 
					                str_data += 4;
 | 
				
			||||||
 | 
					                std::cout << "    [" << j << "] " << std::string((char *) str_data, strlen) << std::endl;
 | 
				
			||||||
 | 
					                str_data += strlen;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            std::cout << "  " << Opus_prettyPacket(part, len) << std::endl;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          offset += len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      std::cout << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int analyseOGG(Util::Config & conf){
 | 
					  int analyseOGG(Util::Config & conf){
 | 
				
			||||||
    std::map<int,std::string> sn2Codec;
 | 
					    std::map<int,std::string> sn2Codec;
 | 
				
			||||||
    std::string oggBuffer;
 | 
					    std::string oggBuffer;
 | 
				
			||||||
| 
						 | 
					@ -242,6 +403,7 @@ namespace Analysers{
 | 
				
			||||||
    sysinfo(&sinfo);
 | 
					    sysinfo(&sinfo);
 | 
				
			||||||
    long long int upTime = sinfo.uptime;
 | 
					    long long int upTime = sinfo.uptime;
 | 
				
			||||||
    while (std::cin.good()){
 | 
					    while (std::cin.good()){
 | 
				
			||||||
 | 
					     oggBuffer.reserve(1024);
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
      for (unsigned int i = 0; (i < 1024) && (std::cin.good()); i++){
 | 
					      for (unsigned int i = 0; (i < 1024) && (std::cin.good()); i++){
 | 
				
			||||||
        oggBuffer += std::cin.get();
 | 
					        oggBuffer += std::cin.get();
 | 
				
			||||||
| 
						 | 
					@ -286,7 +448,7 @@ namespace Analysers{
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      //while OGG::page check function read     
 | 
					      //while OGG::page check function read     
 | 
				
			||||||
      //save last time
 | 
					      //save last time0
 | 
				
			||||||
      sysinfo(&sinfo);
 | 
					      sysinfo(&sinfo);
 | 
				
			||||||
      long long int tTime = sinfo.uptime;
 | 
					      long long int tTime = sinfo.uptime;
 | 
				
			||||||
      if((tTime-upTime) > 5 && (tTime-upTime)>(int)(lastTime)  ){
 | 
					      if((tTime-upTime) > 5 && (tTime-upTime)>(int)(lastTime)  ){
 | 
				
			||||||
| 
						 | 
					@ -307,15 +469,23 @@ namespace Analysers{
 | 
				
			||||||
int main(int argc, char ** argv){
 | 
					int main(int argc, char ** argv){
 | 
				
			||||||
  Util::Config conf = Util::Config(argv[0]);
 | 
					  Util::Config conf = Util::Config(argv[0]);
 | 
				
			||||||
  conf.addOption("pages", JSON::fromString("{\"long\":\"pages\", \"short\":\"p\", \"long_off\":\"nopages\", \"short_off\":\"P\", \"default\":0, \"help\":\"Enable/disable printing of Ogg pages\"}"));
 | 
					  conf.addOption("pages", JSON::fromString("{\"long\":\"pages\", \"short\":\"p\", \"long_off\":\"nopages\", \"short_off\":\"P\", \"default\":0, \"help\":\"Enable/disable printing of Ogg pages\"}"));
 | 
				
			||||||
  conf.addOption("analyse", JSON::fromString("{\"long\":\"analyse\", \"short\":\"a\", \"default\":1, \"long_off\":\"notanalyse\", \"short_off\":\"b\", \"help\":\"Analyse a file's contents (-a), or don't (-b) returning false on error. Default is analyse.\"}"));
 | 
					  conf.addOption("analyse", JSON::fromString("{\"long\":\"analyse\", \"short\":\"a\", \"default\":0, \"long_off\":\"notanalyse\", \"short_off\":\"b\", \"help\":\"Analyse a file's contents (-a), or don't (-b) returning false on error. Default is analyse.\"}"));
 | 
				
			||||||
  conf.addOption("validate", JSON::fromString("{\"long\":\"validate\", \"short\":\"V\", \"default\":0, \"long_off\":\"notvalidate\", \"short_off\":\"x\", \"help\":\"Validate (-V) the file contents or don't validate (-X) its integrity, returning false on error. Default is don't validate.\"}"));
 | 
					  conf.addOption("validate", JSON::fromString("{\"long\":\"validate\", \"short\":\"V\", \"default\":0, \"long_off\":\"notvalidate\", \"short_off\":\"x\", \"help\":\"Validate (-V) the file contents or don't validate (-X) its integrity, returning false on error. Default is don't validate.\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conf.addOption("newfunc", JSON::fromString("{\"long\":\"newfunc\", \"short\":\"N\", \"default\":0, \"long_off\":\"notnewfunc\", \"short_off\":\"x\", \"help\":\"newfuncValidate (-N) the file contents or don't validate (-X) its integrity, returning false on error. Default is don't validate.\"}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  conf.parseArgs(argc, argv);
 | 
					  conf.parseArgs(argc, argv);
 | 
				
			||||||
  conf.activate();
 | 
					  conf.activate();
 | 
				
			||||||
  if (conf.getBool("validate")){
 | 
					  if (conf.getBool("validate")){
 | 
				
			||||||
    return Analysers::validateOGG(conf.getBool("analyse"));
 | 
					    return Analysers::validateOGG(conf.getBool("analyse"));
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    //return Analysers::newfunc(conf);
 | 
				
			||||||
  }else if(conf.getBool("analyse")){
 | 
					  }else if(conf.getBool("analyse")){
 | 
				
			||||||
    return Analysers::analyseOGG(conf);
 | 
					    return Analysers::analyseOGG(conf);
 | 
				
			||||||
 | 
					  }else if(conf.getBool("newfunc")){
 | 
				
			||||||
 | 
					    fprintf(stdout, "begin newfunc\n");
 | 
				
			||||||
 | 
					    return Analysers::newfunc(conf);
 | 
				
			||||||
 | 
					    fprintf(stdout, "end newfunction\n");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue