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()
|
||||
|
||||
makeAnalyser(RTMP rtmp)
|
||||
makeAnalyser(FLV flv)
|
||||
makeAnalyser(DTSC dtsc)
|
||||
makeAnalyser(AMF amf)
|
||||
makeAnalyser(MP4 mp4)
|
||||
add_executable(MistAnalyserMP4 src/analysers/analyser_mp4.cpp src/analysers/analyser.cpp)
|
||||
target_link_libraries(MistAnalyserMP4 mist)
|
||||
install(
|
||||
TARGETS MistAnalyserMP4
|
||||
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(RAX rax)
|
||||
#makeAnalyser(RTP rtp) #LTS
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <string>
|
||||
#include <mist/amf.h>
|
||||
#include <mist/config.h>
|
||||
#include <mist/defines.h>
|
||||
|
||||
///\brief Holds everything unique to the analysers.
|
||||
namespace Analysers {
|
||||
|
@ -15,7 +16,22 @@ namespace Analysers {
|
|||
///
|
||||
/// Expects AMF data through stdin, outputs human-readable information to stderr.
|
||||
///\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;
|
||||
//Read all of std::cin to amfBuffer
|
||||
while (std::cin.good()){
|
||||
|
@ -33,8 +49,9 @@ namespace Analysers {
|
|||
|
||||
int main(int argc, char ** argv){
|
||||
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);
|
||||
|
||||
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,6 +15,54 @@
|
|||
#include <mist/config.h>
|
||||
#include <mist/timing.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <mist/defines.h>
|
||||
|
||||
|
||||
namespace Analysers {
|
||||
|
||||
int analyseFLV(Util::Config conf){
|
||||
bool fileinput = conf.getString("filename").length() > 0;
|
||||
bool analyse = conf.getString("mode") == "analyse";
|
||||
bool validate = conf.getString("mode") == "validate";
|
||||
|
||||
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.
|
||||
long long int endTime = 0;
|
||||
long long int upTime = Util::bootSecs();
|
||||
|
||||
while(!feof(stdin)){
|
||||
if (flvData.FileLoader(stdin)){
|
||||
if (analyse){
|
||||
if (!filter || filter == flvData.data[0]){
|
||||
std::cout << "[" << flvData.tagTime() << "+" << flvData.offset() << "] " << flvData.tagType() << std::endl;
|
||||
}
|
||||
}
|
||||
endTime = flvData.tagTime();
|
||||
}
|
||||
}
|
||||
|
||||
long long int finTime = Util::bootSecs();
|
||||
if (validate){
|
||||
std::cout << upTime << ", " << finTime << ", " << (finTime-upTime) << ", " << endTime << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
///Debugging tool for FLV data.
|
||||
/// Expects FLV data through stdin, outputs human-readable information to stderr.
|
||||
|
@ -22,28 +70,10 @@ 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);
|
||||
bool analyse = conf.getString("mode") == "analyse";
|
||||
bool validate = conf.getString("mode") == "validate";
|
||||
|
||||
long long filter = conf.getInteger("filter");
|
||||
|
||||
FLV::Tag flvData; // Temporary storage for incoming FLV data.
|
||||
long long int endTime = 0;
|
||||
long long int upTime = Util::bootSecs();
|
||||
while ( !feof(stdin)){
|
||||
if (flvData.FileLoader(stdin)){
|
||||
if (analyse){
|
||||
if (!filter || filter == flvData.data[0]){
|
||||
std::cout << "[" << flvData.tagTime() << "+" << flvData.offset() << "] " << flvData.tagType() << std::endl;
|
||||
}
|
||||
}
|
||||
endTime = flvData.tagTime();
|
||||
}
|
||||
}
|
||||
long long int finTime = Util::bootSecs();
|
||||
if (validate){
|
||||
std::cout << upTime << ", " << finTime << ", " << (finTime-upTime) << ", " << endTime << std::endl;
|
||||
}
|
||||
return 0;
|
||||
return Analysers::analyseFLV(conf);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,42 @@ namespace Analysers {
|
|||
///
|
||||
/// Expects MP4 data through stdin, outputs human-readable information to stderr.
|
||||
///\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;
|
||||
//Read all of std::cin to mp4Buffer
|
||||
while (std::cin.good()){
|
||||
|
@ -25,15 +60,19 @@ namespace Analysers {
|
|||
}
|
||||
mp4Buffer.erase(mp4Buffer.size() - 1, 1);
|
||||
|
||||
|
||||
MP4::Box mp4Data;
|
||||
int dataSize = mp4Buffer.size();
|
||||
int curPos = 0;
|
||||
|
||||
while (mp4Data.read(mp4Buffer)){
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
DEBUG_MSG(DLVL_DEVEL, "Stopped parsing at position %d", curPos);
|
||||
return 0;
|
||||
}
|
||||
|
@ -43,7 +82,8 @@ namespace Analysers {
|
|||
/// Expects MP4 data through stdin, outputs human-readable information to stderr.
|
||||
int main(int argc, char ** argv){
|
||||
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);
|
||||
return Analysers::analyseMP4();
|
||||
return Analysers::analyseMP4(conf);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,167 @@ namespace Analysers{
|
|||
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){
|
||||
std::map<int,std::string> sn2Codec;
|
||||
std::string oggBuffer;
|
||||
|
@ -227,7 +388,7 @@ namespace Analysers{
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int validateOGG(bool analyse){
|
||||
std::map<int,std::string> sn2Codec;
|
||||
std::string oggBuffer;
|
||||
|
@ -242,7 +403,8 @@ namespace Analysers{
|
|||
sysinfo(&sinfo);
|
||||
long long int upTime = sinfo.uptime;
|
||||
while (std::cin.good()){
|
||||
|
||||
oggBuffer.reserve(1024);
|
||||
|
||||
for (unsigned int i = 0; (i < 1024) && (std::cin.good()); i++){
|
||||
oggBuffer += std::cin.get();
|
||||
}
|
||||
|
@ -286,7 +448,7 @@ namespace Analysers{
|
|||
}
|
||||
}
|
||||
//while OGG::page check function read
|
||||
//save last time
|
||||
//save last time0
|
||||
sysinfo(&sinfo);
|
||||
long long int tTime = sinfo.uptime;
|
||||
if((tTime-upTime) > 5 && (tTime-upTime)>(int)(lastTime) ){
|
||||
|
@ -307,15 +469,23 @@ namespace Analysers{
|
|||
int main(int argc, char ** argv){
|
||||
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("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("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.activate();
|
||||
if (conf.getBool("validate")){
|
||||
return Analysers::validateOGG(conf.getBool("analyse"));
|
||||
|
||||
//return Analysers::newfunc(conf);
|
||||
}else if(conf.getBool("analyse")){
|
||||
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
Reference in a new issue