From a47504b5cb64546c0f5fd320ad8667c64ef236c7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 23 Dec 2014 13:10:28 +0100 Subject: [PATCH] Ogg support fixed and re-added. Squash of various commits made by Wouter Spruit. --- Makefile | 23 +- src/analysers/dtsc_analyser.cpp | 2 +- src/analysers/ogg_analyser.cpp | 272 +++++++----- src/controller/controller_streams.cpp | 2 +- src/input/input.cpp | 134 +++--- src/input/input.h | 4 +- src/input/input_buffer.cpp | 8 +- src/input/input_ogg.cpp | 603 ++++++++++++++++---------- src/input/input_ogg.h | 66 ++- src/output/output.cpp | 22 +- src/output/output.h | 6 +- src/output/output_hds.cpp | 2 +- src/output/output_hls.cpp | 6 +- src/output/output_hss.cpp | 12 +- src/output/output_json.cpp | 2 +- src/output/output_progressive_ogg.cpp | 208 +++++++++ src/output/output_progressive_ogg.h | 28 ++ 17 files changed, 944 insertions(+), 456 deletions(-) create mode 100644 src/output/output_progressive_ogg.cpp create mode 100644 src/output/output_progressive_ogg.h diff --git a/Makefile b/Makefile index ef8bb5ec..85781a22 100644 --- a/Makefile +++ b/Makefile @@ -57,9 +57,9 @@ analysers: MistAnalyserMP4 MistAnalyserMP4: src/analysers/mp4_analyser.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ -#analysers: MistAnalyserOGG -#MistAnalyserOGG: src/analysers/ogg_analyser.cpp -# $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ +analysers: MistAnalyserOGG +MistAnalyserOGG: src/analysers/ogg_analyser.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ analysers: MistInfo MistInfo: src/analysers/info.cpp @@ -77,11 +77,11 @@ MistInFLV: override CPPFLAGS += "-DINPUTTYPE=\"input_flv.h\"" MistInFLV: src/input/mist_in.cpp src/input/input.cpp src/input/input_flv.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ -#inputs: MistInOGG -#MistInOGG: override LDLIBS += $(THREADLIB) -#MistInOGG: override CPPFLAGS += "-DINPUTTYPE=\"input_ogg.h\"" -#MistInOGG: src/input/mist_in.cpp src/input/input.cpp src/input/input_ogg.cpp -# $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ +inputs: MistInOGG +MistInOGG: override LDLIBS += $(THREADLIB) +MistInOGG: override CPPFLAGS += "-DINPUTTYPE=\"input_ogg.h\"" +MistInOGG: src/input/mist_in.cpp src/input/input.cpp src/input/input_ogg.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ inputs: MistInBuffer MistInBuffer: override LDLIBS += $(THREADLIB) @@ -95,6 +95,13 @@ MistOutFLV: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_flv.h\"" MistOutFLV: src/output/mist_out.cpp src/output/output_http.cpp src/output/output.cpp src/output/output_progressive_flv.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ +outputs: MistOutOGG +MistOutOGG: override LDLIBS += $(THREADLIB) +MistOutOGG: override LDLIBS += $(GEOIP) +MistOutOGG: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_ogg.h\"" +MistOutOGG: src/output/mist_out.cpp src/output/output_http.cpp src/output/output.cpp src/output/output_progressive_ogg.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + outputs: MistOutMP4 MistOutMP4: override LDLIBS += $(THREADLIB) MistOutMP4: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp4.h\"" diff --git a/src/analysers/dtsc_analyser.cpp b/src/analysers/dtsc_analyser.cpp index 7b1cd087..57df2df1 100644 --- a/src/analysers/dtsc_analyser.cpp +++ b/src/analysers/dtsc_analyser.cpp @@ -35,7 +35,7 @@ namespace Analysers { break; } case DTSC::DTSC_V2: { - std::cout << "DTSCv2 packet: " << F.getPacket().getScan().toPrettyString() << std::endl; + std::cout << "DTSCv2 packet (Track " << F.getPacket().getTrackId() << ", time " << F.getPacket().getTime() << "): " << F.getPacket().getScan().toPrettyString() << std::endl; break; } case DTSC::DTSC_HEAD: { diff --git a/src/analysers/ogg_analyser.cpp b/src/analysers/ogg_analyser.cpp index 8fd881a2..c7b2df91 100644 --- a/src/analysers/ogg_analyser.cpp +++ b/src/analysers/ogg_analyser.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -8,48 +8,90 @@ #include #include #include - -namespace Analysers{ - std::string Opus_prettyPacket(char * part,int len){ +///\todo rewrite this analyser. +namespace Analysers { + std::string Opus_prettyPacket(const char * part, int len){ if (len < 1){ return "Invalid packet (0 byte length)"; } std::stringstream r; char config = part[0] >> 3; char code = part[0] & 3; - if ((part[0] & 4) == 4){r << "Stereo, ";}else{r << "Mono, ";} + if ((part[0] & 4) == 4){ + r << "Stereo, "; + } else { + r << "Mono, "; + } if (config < 14){ r << "SILK, "; - if (config < 4){r << "NB, ";} - if (config < 8 && config > 3){r << "MB, ";} - if (config < 14 && config > 7){r << "WB, ";} - if (config % 4 == 0){r << "10ms";} - if (config % 4 == 1){r << "20ms";} - if (config % 4 == 2){r << "40ms";} - if (config % 4 == 3){r << "60ms";} + if (config < 4){ + r << "NB, "; + } + if (config < 8 && config > 3){ + r << "MB, "; + } + if (config < 14 && config > 7){ + r << "WB, "; + } + if (config % 4 == 0){ + r << "10ms"; + } + if (config % 4 == 1){ + r << "20ms"; + } + if (config % 4 == 2){ + r << "40ms"; + } + if (config % 4 == 3){ + r << "60ms"; + } } if (config < 16 && config > 13){ r << "Hybrid, "; - if (config < 14){r << "SWB, ";}else{r << "FB, ";} - if (config % 2 == 0){r << "10ms";}else{r << "20ms";} + if (config < 14){ + r << "SWB, "; + } else { + r << "FB, "; + } + if (config % 2 == 0){ + r << "10ms"; + } else { + r << "20ms"; + } } if (config > 15){ r << "CELT, "; - if (config < 20){r << "NB, ";} - if (config < 24 && config > 19){r << "WB, ";} - if (config < 28 && config > 23){r << "SWB, ";} - if (config > 27){r << "FB, ";} - if (config % 4 == 0){r << "2.5ms";} - if (config % 4 == 1){r << "5ms";} - if (config % 4 == 2){r << "10ms";} - if (config % 4 == 3){r << "20ms";} + if (config < 20){ + r << "NB, "; + } + if (config < 24 && config > 19){ + r << "WB, "; + } + if (config < 28 && config > 23){ + r << "SWB, "; + } + if (config > 27){ + r << "FB, "; + } + if (config % 4 == 0){ + r << "2.5ms"; + } + if (config % 4 == 1){ + r << "5ms"; + } + if (config % 4 == 2){ + r << "10ms"; + } + if (config % 4 == 3){ + r << "20ms"; + } } if (code == 0){ - r << ": 1 packet (" << (len-1) << "b)"; + r << ": 1 packet (" << (len - 1) << "b)"; return r.str(); } if (code == 1){ - r << ": 2 packets (" << ((len-1)/2) << "b / " << ((len-1)/2) << "b)"; + r << ": 2 packets (" << ((len - 1) / 2) << "b / " << ((len - 1) / 2) << "b)"; return r.str(); } if (code == 2){ @@ -57,10 +99,10 @@ namespace Analysers{ return "Invalid packet (code 2 must be > 1 byte long)"; } if (part[1] < 252){ - r << ": 2 packets (" << (int)part[1] << "b / " << (int)(len-2-part[1]) << "b)"; - }else{ - int ilen = part[1] + part[2]*4; - r << ": 2 packets (" << ilen << "b / " << (int)(len-3-ilen) << "b)"; + r << ": 2 packets (" << (int)part[1] << "b / " << (int)(len - 2 - part[1]) << "b)"; + } else { + int ilen = part[1] + part[2] * 4; + r << ": 2 packets (" << ilen << "b / " << (int)(len - 3 - ilen) << "b)"; } return r.str(); } @@ -71,112 +113,115 @@ namespace Analysers{ r << ": " << packets << " packets (VBR = " << VBR << ", padding = " << pad << ")"; return r.str(); } - + int analyseOGG(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); 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.parseArgs(argc, argv); - - std::map sn2Codec; + + std::map sn2Codec; std::string oggBuffer; OGG::Page oggPage; + int kfgshift; //Read all of std::cin to oggBuffer - //while stream busy - while (std::cin.good()){ - for (unsigned int i = 0; (i < 1024) && (std::cin.good()); i++){ - oggBuffer += std::cin.get(); + //while OGG::page check function read + 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()); } - //while OGG::page check function read - while (oggPage.read(oggBuffer)){//reading ogg to string - //print the Ogg page details, if requested - if (conf.getBool("pages")){ - std::cout << oggPage.toPrettyString() << std::endl; + + //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"; } - - //attempt to detect codec if this is the first page of a stream - if (oggPage.getHeaderType() & OGG::BeginOfStream){ - if (memcmp("theora",oggPage.getFullPayload() + 1,6) == 0){ - sn2Codec[oggPage.getBitstreamSerialNumber()] = "Theora"; - } - if (memcmp("vorbis",oggPage.getFullPayload() + 1,6) == 0){ - sn2Codec[oggPage.getBitstreamSerialNumber()] = "Vorbis"; - } - if (memcmp("OpusHead",oggPage.getFullPayload(),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 (memcmp("vorbis", oggPage.getSegment(0) + 1, 6) == 0){ + sn2Codec[oggPage.getBitstreamSerialNumber()] = "Vorbis"; } - - if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Theora"){ - std::cout << "Theora data" << std::endl; - int offset = 0; - for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ - theora::header tmpHeader; - int len = oggPage.getSegmentTableDeque()[i]; - if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){ - std::cout << tmpHeader.toPrettyString(2); + 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(); } - theora::frame tmpFrame; - if (tmpFrame.read(oggPage.getFullPayload()+offset,len)){ - std::cout << tmpFrame.toPrettyString(2); + } 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++; } - offset += len; } - }else if(sn2Codec[oggPage.getBitstreamSerialNumber()] == "Vorbis"){ - std::cout << "Vorbis data" << std::endl; - int offset = 0; - for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ - vorbis::header tmpHeader; - int len = oggPage.getSegmentTableDeque()[i]; - if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){ - std::cout << tmpHeader.toPrettyString(2); - } - offset += len; + 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.getSegmentTableDeque().size(); i++){ - int len = oggPage.getSegmentTableDeque()[i]; - char * part = oggPage.getFullPayload() + offset; - 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; - } + } + } 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; - 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; + } + 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; - 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(str_data, strlen) << std::endl; - str_data += strlen; - } + 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; + } else { + std::cout << " " << Opus_prettyPacket(part, len) << std::endl; } + offset += len; } } + std::cout << std::endl; } return 0; } @@ -186,3 +231,4 @@ int main(int argc, char ** argv){ return Analysers::analyseOGG(argc, argv); } + diff --git a/src/controller/controller_streams.cpp b/src/controller/controller_streams.cpp index a3f3d39c..e503c3a5 100644 --- a/src/controller/controller_streams.cpp +++ b/src/controller/controller_streams.cpp @@ -110,7 +110,7 @@ namespace Controller { data["online"] = 0; return; }else{ - if (!getMeta && data["meta"].packedSize() > 1*1024 * data["meta"]["tracks"].size()){ + if (!getMeta && data["meta"].packedSize() > 10*1024 * data["meta"]["tracks"].size()){ DEBUG_MSG(DLVL_WARN, "Metadata for stream %s is quite big (%u b) - assuming corruption and forcing reload", name.c_str(), data["meta"].packedSize()); getMeta = true; } diff --git a/src/input/input.cpp b/src/input/input.cpp index d51949c0..27fcd975 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -13,9 +13,9 @@ namespace Mist { void Input::userCallback(char * data, size_t len, unsigned int id){ for (int i = 0; i < 5; i++){ - long tid = ((long)(data[i*6]) << 24) | ((long)(data[i*6+1]) << 16) | ((long)(data[i*6+2]) << 8) | ((long)(data[i*6+3])); + unsigned long tid = ((unsigned long)(data[i*6]) << 24) | ((unsigned long)(data[i*6+1]) << 16) | ((unsigned long)(data[i*6+2]) << 8) | ((unsigned long)(data[i*6+3])); if (tid){ - long keyNum = ((long)(data[i*6+4]) << 8) | ((long)(data[i*6+5])); + unsigned long keyNum = ((unsigned long)(data[i*6+4]) << 8) | ((unsigned long)(data[i*6+5])); bufferFrame(tid, keyNum + 1);//Try buffer next frame } } @@ -155,7 +155,7 @@ namespace Mist { if (!isBuffer){ - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ bufferFrame(it->first, 1); } } @@ -204,8 +204,8 @@ namespace Mist { DEBUG_MSG(DLVL_DONTEVEN,"Parsing the header"); selectedTracks.clear(); std::stringstream trackSpec; - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ - DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first); + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + DEBUG_MSG(DLVL_VERYHIGH, "Track %u encountered", it->first); if (trackSpec.str() != ""){ trackSpec << " "; } @@ -218,16 +218,18 @@ namespace Mist { trackSelect(trackSpec.str()); bool hasKeySizes = true; - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ if (!it->second.keySizes.size()){ hasKeySizes = false; break; } } + INFO_MSG("%s", (hasKeySizes ? "hasKeysizes" : "noHasKeysizes")); if (hasKeySizes){ - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ char tmpId[20]; - sprintf(tmpId, "%d", it->first); + sprintf(tmpId, "%u", it->first); + INFO_MSG("Making page %s", std::string(config->getString("streamname") + tmpId).c_str()); indexPages[it->first].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts. bool newData = true; for (int i = 0; i < it->second.keys.size(); i++){ @@ -245,68 +247,72 @@ namespace Mist { } } }else{ - std::map curData; - std::map bookKeeping; - - seek(0); - getNext(); + std::map curData; + std::map bookKeeping; + + seek(0); + getNext(); - while(lastPack){//loop through all - int tid = lastPack.getTrackId(); - if (!tid){ - getNext(false); - continue; - } - if (!bookKeeping.count(tid)){ - bookKeeping[tid].first = 1; - bookKeeping[tid].curPart = 0; - bookKeeping[tid].curKey = 0; - - curData[tid].lastKeyTime = 0xFFFFFFFF; - curData[tid].keyNum = 1; - curData[tid].partNum = 0; - curData[tid].dataSize = 0; - curData[tid].curOffset = 0; - curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime(); - - char tmpId[20]; - sprintf(tmpId, "%d", tid); - indexPages[tid].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts. - } - if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){ - if (curData[tid].dataSize > 8 * 1024 * 1024){ - pagesByTrack[tid][bookKeeping[tid].first] = curData[tid]; - bookKeeping[tid].first += curData[tid].keyNum; - curData[tid].keyNum = 0; - curData[tid].dataSize = 0; - curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime(); - } - bookKeeping[tid].curKey++; - curData[tid].keyNum++; - curData[tid].partNum = 0; - } - curData[tid].dataSize += lastPack.getDataLen(); - curData[tid].partNum ++; - bookKeeping[tid].curPart ++; - DEBUG_MSG(DLVL_INSANE, "Track %ld:%llu (%db) on page %d, being part %d of key %d", lastPack.getTrackId(), lastPack.getTime(), lastPack.getDataLen(), bookKeeping[tid].first, curData[tid].partNum, curData[tid].keyNum); + while(lastPack){//loop through all + unsigned int tid = lastPack.getTrackId(); + if (!tid){ getNext(false); + continue; } - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ - if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){ - pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first]; + if (!bookKeeping.count(tid)){ + bookKeeping[tid].first = 1; + bookKeeping[tid].curPart = 0; + bookKeeping[tid].curKey = 0; + + curData[tid].lastKeyTime = 0xFFFFFFFF; + curData[tid].keyNum = 1; + curData[tid].partNum = 0; + curData[tid].dataSize = 0; + curData[tid].curOffset = 0; + curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime(); + + char tmpId[80]; + snprintf(tmpId, 80, "%s%u", config->getString("streamname").c_str(), tid); + indexPages[tid].init(tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts. + } + if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() + 1 == curData[tid].partNum){ + if (curData[tid].dataSize > 8 * 1024 * 1024) { + pagesByTrack[tid][bookKeeping[tid].first] = curData[tid]; + bookKeeping[tid].first += curData[tid].keyNum; + curData[tid].keyNum = 0; + curData[tid].dataSize = 0; + curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime(); } + bookKeeping[tid].curKey++; + curData[tid].keyNum++; + curData[tid].partNum = 0; + } + curData[tid].dataSize += lastPack.getDataLen(); + curData[tid].partNum ++; + bookKeeping[tid].curPart ++; + DEBUG_MSG(DLVL_DONTEVEN, "Track %ld:%llu on page %d@%llu (len:%d), being part %d of key %d", lastPack.getTrackId(), lastPack.getTime(), bookKeeping[tid].first, curData[tid].dataSize, lastPack.getDataLen(), curData[tid].partNum, bookKeeping[tid].first+curData[tid].keyNum); + getNext(false); + } + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){ + pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first]; } } - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ - DEBUG_MSG(DLVL_MEDIUM, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size()); - for (std::map::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){ - DEBUG_MSG(DLVL_VERYHIGH, "Page %u-%u, (%llu bytes)", it2->first, it2->first + it2->second.keyNum - 1, it2->second.dataSize); + } + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + if (!pagesByTrack.count(it->first)){ + DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first); + }else{ + DEBUG_MSG(DLVL_MEDIUM, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size()); + for (std::map::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){ + DEBUG_MSG(DLVL_VERYHIGH, "Page %u-%u, (%llu bytes)", it2->first, it2->first + it2->second.keyNum - 1, it2->second.dataSize); + } } } } - bool Input::bufferFrame(int track, int keyNum){ + bool Input::bufferFrame(unsigned int track, unsigned int keyNum){ if (keyNum < 1){keyNum = 1;} if (!pagesByTrack.count(track)){ return false; @@ -315,21 +321,22 @@ namespace Mist { if (it != pagesByTrack[track].begin()){ it--; } - int pageNum = it->first; + unsigned int pageNum = it->first; pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use - DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer %d:%d on page %d", track, keyNum, pageNum); + DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %u key %d->%d", track, keyNum, pageNum); if (dataPages[track].count(pageNum)){ return true; } char pageId[100]; - int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum); + int pageIdLen = snprintf(pageId, 100, "%s%u_%u", config->getString("streamname").c_str(), track, pageNum); std::string tmpString(pageId, pageIdLen); #ifdef __CYGWIN__ dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true); #else dataPages[track][pageNum].init(tmpString, it->second.dataSize, true); #endif + DEBUG_MSG(DLVL_HIGH, "Buffering track %u page %u through %u datasize: %llu", track, pageNum, pageNum-1 + it->second.keyNum, it->second.dataSize); std::stringstream trackSpec; trackSpec << track; @@ -354,6 +361,7 @@ namespace Mist { DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum); break; }else{ +// DEBUG_MSG(DLVL_WARN, "Writing %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum); memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen()); it->second.curOffset += lastPack.getDataLen(); } @@ -365,7 +373,7 @@ namespace Mist { break; } } - DEBUG_MSG(DLVL_HIGH, "Done buffering page %d for track %d", pageNum, track); + DEBUG_MSG(DLVL_DEVEL, "Done buffering page %u for track %u", pageNum, track); return true; } diff --git a/src/input/input.h b/src/input/input.h index c961c6dc..8209c598 100644 --- a/src/input/input.h +++ b/src/input/input.h @@ -45,7 +45,7 @@ namespace Mist { virtual void userCallback(char * data, size_t len, unsigned int id); void parseHeader(); - bool bufferFrame(int track, int keyNum); + bool bufferFrame(unsigned int track, unsigned int keyNum); unsigned int packTime;///Media-timestamp of the last packet. int lastActive;///Timestamp of the last time we received or sent something. @@ -53,7 +53,7 @@ namespace Mist { int playing; unsigned int playUntil; unsigned int benchMark; - std::set selectedTracks; + std::set selectedTracks; bool isBuffer; diff --git a/src/input/input_buffer.cpp b/src/input/input_buffer.cpp index dd2666be..77208229 100644 --- a/src/input/input_buffer.cpp +++ b/src/input/input_buffer.cpp @@ -43,7 +43,7 @@ namespace Mist { inputBuffer::~inputBuffer(){ if (myMeta.tracks.size()){ DEBUG_MSG(DLVL_DEVEL, "Cleaning up, removing last keyframes"); - for(std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for(std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ while (removeKey(it->first)){} } } @@ -52,7 +52,7 @@ namespace Mist { void inputBuffer::updateMeta(){ long long unsigned int firstms = 0xFFFFFFFFFFFFFFFFull; long long unsigned int lastms = 0; - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ if (it->second.firstms < firstms){ firstms = it->second.firstms; } @@ -116,14 +116,14 @@ namespace Mist { void inputBuffer::removeUnused(){ //find the earliest video keyframe stored unsigned int firstVideo = 1; - for(std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for(std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ if (it->second.type == "video"){ if (it->second.firstms < firstVideo || firstVideo == 1){ firstVideo = it->second.firstms; } } } - for(std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for(std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ //non-video tracks need to have a second keyframe that is <= firstVideo if (it->second.type != "video"){ if (it->second.keys.size() < 2 || it->second.keys[1].getTime() > firstVideo){ diff --git a/src/input/input_ogg.cpp b/src/input/input_ogg.cpp index 0c774cb7..d4fe6099 100644 --- a/src/input/input_ogg.cpp +++ b/src/input/input_ogg.cpp @@ -9,269 +9,426 @@ #include #include #include - #include "input_ogg.h" +///\todo Whar be Opus support? + namespace Mist { - inputOGG::inputOGG(Util::Config * cfg) : Input(cfg) { + + JSON::Value segment::toJSON(OGG::oggCodec myCodec){ + JSON::Value retval; + retval["time"] = (long long int)time; + retval["trackid"] = (long long int)tid; + std::string tmpString = ""; + for (unsigned int i = 0; i < parts.size(); i++){ + tmpString += parts[i]; + } + retval["data"] = tmpString; +// INFO_MSG("Setting bpos for packet on track %llu, time %llu, to %llu", tid, time, bytepos); + retval["bpos"] = (long long int)bytepos; + if (myCodec == OGG::THEORA){ + if (!theora::isHeader(tmpString.data(), tmpString.size())){ + theora::header tmpHeader((char*)tmpString.data(), tmpString.size()); + if (tmpHeader.getFTYPE() == 0){ + retval["keyframe"] = 1LL; + } + } + } + return retval; + } + +/* + unsigned long oggTrack::getBlockSize(unsigned int vModeIndex){ //WTF!!? + return blockSize[vModes[vModeIndex].blockFlag]; + + } +*/ + inputOGG::inputOGG(Util::Config * cfg) : Input(cfg){ capa["name"] = "OGG"; - capa["decs"] = "Enables OGG Input"; + capa["desc"] = "Enables OGG input"; capa["codecs"][0u][0u].append("theora"); capa["codecs"][0u][1u].append("vorbis"); } - bool inputOGG::setup() { - if (config->getString("input") == "-") { - std::cerr << "Input from stdin not yet supported" << std::endl; + bool inputOGG::setup(){ + if (config->getString("input") == "-"){ + std::cerr << "Input from stream not yet supported" << std::endl; return false; } - if (!config->getString("streamname").size()){ - if (config->getString("output") == "-") { - std::cerr << "Output to stdout not yet supported" << std::endl; - return false; - } - }else{ - if (config->getString("output") != "-") { - std::cerr << "File output in player mode not supported" << std::endl; - return false; - } - } - + //open File inFile = fopen(config->getString("input").c_str(), "r"); - if (!inFile) { + if (!inFile){ return false; } return true; } - void inputOGG::parseBeginOfStream(OGG::Page & bosPage) { - long long int tid = snum2tid.size() + 1; - snum2tid[bosPage.getBitstreamSerialNumber()] = tid; - if (!memcmp(bosPage.getFullPayload() + 1, "theora", 6)) { - oggTracks[tid].codec = THEORA; - theora::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize()); - oggTracks[tid].msPerFrame = (double)(tmpHead.getFRD() * 1000) / tmpHead.getFRN(); + ///\todo check if all trackID (tid) instances are replaced with bitstream serial numbers + void inputOGG::parseBeginOfStream(OGG::Page & bosPage){ + //long long int tid = snum2tid.size() + 1; + unsigned int tid = bosPage.getBitstreamSerialNumber(); + if (memcmp(bosPage.getSegment(0) + 1, "theora", 6) == 0){ + theora::header tmpHead((char*)bosPage.getSegment(0), bosPage.getSegmentLen(0)); + oggTracks[tid].codec = OGG::THEORA; + oggTracks[tid].msPerFrame = (double)(tmpHead.getFRD() * 1000) / (double)tmpHead.getFRN(); //this should be: 1000/( tmpHead.getFRN()/ tmpHead.getFRD() ) + DEBUG_MSG(DLVL_DEVEL, "theora trackID: %u msperFrame %f ", tid, oggTracks[tid].msPerFrame); + oggTracks[tid].KFGShift = tmpHead.getKFGShift(); //store KFGShift for granule calculations + DEVEL_MSG("read theora header size: %d, tid: %u", tmpHead.datasize, tid); + + myMeta.tracks[tid].type = "video"; + myMeta.tracks[tid].codec = "theora"; + myMeta.tracks[tid].trackID = tid; + myMeta.tracks[tid].fpks = (tmpHead.getFRN() * 1000) / tmpHead.getFRD(); + myMeta.tracks[tid].height = tmpHead.getPICH(); + myMeta.tracks[tid].width = tmpHead.getPICW(); + if (!myMeta.tracks[tid].init.size()){ + myMeta.tracks[tid].init = (char)((bosPage.getPayloadSize() >> 8) & 0xFF); + myMeta.tracks[tid].init += (char)(bosPage.getPayloadSize() & 0xFF); + myMeta.tracks[tid].init.append(bosPage.getSegment(0), bosPage.getSegmentLen(0)); + } } - if (!memcmp(bosPage.getFullPayload() + 1, "vorbis", 6)) { - oggTracks[tid].codec = VORBIS; - vorbis::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize()); - oggTracks[tid].msPerFrame = (double)1000 / ntohl(tmpHead.getAudioSampleRate()); + if (memcmp(bosPage.getSegment(0) + 1, "vorbis", 6) == 0){ + vorbis::header tmpHead((char*)bosPage.getSegment(0), bosPage.getSegmentLen(0)); + + oggTracks[tid].codec = OGG::VORBIS; + oggTracks[tid].msPerFrame = (double)1000.0f / tmpHead.getAudioSampleRate(); + DEBUG_MSG(DLVL_DEVEL, "vorbis trackID: %d msperFrame %f ", tid, oggTracks[tid].msPerFrame); + oggTracks[tid].channels = tmpHead.getAudioChannels(); + oggTracks[tid].blockSize[0] = 1 << tmpHead.getBlockSize0(); + oggTracks[tid].blockSize[1] = 1 << tmpHead.getBlockSize1(); + //Abusing .contBuffer for temporarily storing the idHeader + bosPage.getSegment(0, oggTracks[tid].contBuffer); + + myMeta.tracks[tid].type = "audio"; + myMeta.tracks[tid].codec = "vorbis"; + myMeta.tracks[tid].rate = tmpHead.getAudioSampleRate(); + myMeta.tracks[tid].trackID = tid; + myMeta.tracks[tid].channels = tmpHead.getAudioChannels(); } } - bool inputOGG::readHeader() { - JSON::Value lastPack; - if (!inFile) { - return false; - } - //See whether a separate header file exists. - DTSC::File tmp(config->getString("input") + ".dtsh"); - if (tmp) { - myMeta = tmp.getMeta(); - return true; - } - //Create header file from OGG data + bool inputOGG::readHeader(){ + OGG::Page myPage; fseek(inFile, 0, SEEK_SET); - OGG::Page tmpPage; - long long int lastBytePos = 0; - while (tmpPage.read(inFile)) { - DEBUG_MSG(DLVL_WARN,"Read a page"); - if (tmpPage.getHeaderType() & OGG::BeginOfStream){ - parseBeginOfStream(tmpPage); - DEBUG_MSG(DLVL_WARN,"Read BOS page for stream %lu, now track %lld", tmpPage.getBitstreamSerialNumber(), snum2tid[tmpPage.getBitstreamSerialNumber()]); + while (myPage.read(inFile)){ //assumes all headers are sent before any data + unsigned int tid = myPage.getBitstreamSerialNumber(); + if (myPage.getHeaderType() & OGG::BeginOfStream){ + parseBeginOfStream(myPage); + INFO_MSG("Read BeginOfStream for track %d", tid); + continue; //Continue reading next pages } - int offset = 0; - long long int tid = snum2tid[tmpPage.getBitstreamSerialNumber()]; - for (std::deque::iterator it = tmpPage.getSegmentTableDeque().begin(); it != tmpPage.getSegmentTableDeque().end(); it++) { - if (oggTracks[tid].parsedHeaders) { - DEBUG_MSG(DLVL_WARN,"Parsing a page segment on track %lld", tid); - if ((it == (tmpPage.getSegmentTableDeque().end() - 1)) && (int)(tmpPage.getPageSegments()) == 255 && (int)(tmpPage.getSegmentTable()[254]) == 255) { - oggTracks[tid].contBuffer.append(tmpPage.getFullPayload() + offset, (*it)); - } else { - lastPack["trackid"] = tid; - lastPack["time"] = (long long)oggTracks[tid].lastTime; - if (oggTracks[tid].contBuffer.size()) { - lastPack["data"] = oggTracks[tid].contBuffer + std::string(tmpPage.getFullPayload() + offset, (*it)); - oggTracks[tid].contBuffer.clear(); - } else { - lastPack["data"] = std::string(tmpPage.getFullPayload() + offset, (*it)); - } - if (oggTracks[tid].codec == VORBIS) { - unsigned int blockSize = 0; - Utils::bitstreamLSBF packet; - packet.append(lastPack["data"].asString()); - if (!packet.get(1)) { - blockSize = oggTracks[tid].blockSize[oggTracks[tid].vModes[packet.get(vorbis::ilog(oggTracks[tid].vModes.size() - 1))].blockFlag]; - } else { - DEBUG_MSG(DLVL_WARN, "Packet type != 0"); - } - oggTracks[tid].lastTime += oggTracks[tid].msPerFrame * (blockSize / oggTracks[tid].channels); - } - if (oggTracks[tid].codec == THEORA) { - oggTracks[tid].lastTime += oggTracks[tid].msPerFrame; - if (it == (tmpPage.getSegmentTableDeque().end() - 1)) { - if (oggTracks[tid].idHeader.parseGranuleUpper(oggTracks[tid].lastGran) != oggTracks[tid].idHeader.parseGranuleUpper(tmpPage.getGranulePosition())) { - lastPack["keyframe"] = 1ll; - oggTracks[tid].lastGran = tmpPage.getGranulePosition(); - } else { - lastPack["interframe"] = 1ll; - } - } - } - lastPack["bpos"] = 0ll; - DEBUG_MSG(DLVL_WARN,"Parsed a packet of track %lld, new timestamp %f", tid, oggTracks[tid].lastTime); - myMeta.update(lastPack); - } - } else { - //Parsing headers - switch (oggTracks[tid].codec) { - case THEORA: { - theora::header tmpHead(tmpPage.getFullPayload() + offset, (*it)); - DEBUG_MSG(DLVL_WARN,"Theora header, type %d", tmpHead.getHeaderType()); - switch (tmpHead.getHeaderType()) { - case 0: { - oggTracks[tid].idHeader = tmpHead; - myMeta.tracks[tid].height = tmpHead.getPICH(); - myMeta.tracks[tid].width = tmpHead.getPICW(); - myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); - break; - } - case 1: { - myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); - break; - } - case 2: { - myMeta.tracks[tid].codec = "theora"; - myMeta.tracks[tid].trackID = tid; - myMeta.tracks[tid].type = "video"; - myMeta.tracks[tid].init = std::string(tmpPage.getFullPayload() + offset, (*it)); - oggTracks[tid].parsedHeaders = true; - oggTracks[tid].lastGran = 0; - break; - } - } - break; - } - case VORBIS: { - vorbis::header tmpHead(tmpPage.getFullPayload() + offset, (*it)); - DEBUG_MSG(DLVL_WARN,"Vorbis header, type %d", tmpHead.getHeaderType()); - switch (tmpHead.getHeaderType()) { - case 1: { - myMeta.tracks[tid].channels = tmpHead.getAudioChannels(); - myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); - oggTracks[tid].channels = tmpHead.getAudioChannels(); - oggTracks[tid].blockSize[0] = 1 << tmpHead.getBlockSize0(); - oggTracks[tid].blockSize[1] = 1 << tmpHead.getBlockSize1(); - break; - } - case 3: { - myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); - break; - } - case 5: { - myMeta.tracks[tid].codec = "vorbis"; - myMeta.tracks[tid].trackID = tid; - myMeta.tracks[tid].type = "audio"; - DEBUG_MSG(DLVL_WARN,"Set default values"); - myMeta.tracks[tid].init = std::string(tmpPage.getFullPayload() + offset, (*it)); - DEBUG_MSG(DLVL_WARN,"Set init values"); - oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels); - DEBUG_MSG(DLVL_WARN,"Set vmodevalues"); - oggTracks[tid].parsedHeaders = true; - break; - } - } - break; - } - } - offset += (*it); + + bool readAllHeaders = true; + for (std::map::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){ + if (!it->second.parsedHeaders){ + readAllHeaders = false; + break; + } + } + if (readAllHeaders){ + break; + } + + // INFO_MSG("tid: %d",tid); + + //Parsing headers +// INFO_MSG("parsing headers for tid: %d",tid); + ///\todo make sure the header is ffmpeg compatible + if (myMeta.tracks[tid].codec == "theora"){ + // INFO_MSG("theora"); + for (int i = 0; i < myPage.getAllSegments().size(); i++){ + unsigned long len = myPage.getSegmentLen(i); + INFO_MSG("Length for segment %d: %lu", i, len); + theora::header tmpHead((char*)myPage.getSegment(i),len); + if (!tmpHead.isHeader()){ //not copying the header anymore, should this check isHeader? + DEBUG_MSG(DLVL_FAIL, "Theora Header read failed!"); + return false; + } + DEBUG_MSG(DLVL_WARN, "Theora header, type %d", tmpHead.getHeaderType()); + switch (tmpHead.getHeaderType()){ + //Case 0 is being handled by parseBeginOfStream + case 1: { + myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF); + myMeta.tracks[tid].init += (char)(len & 0xFF); + myMeta.tracks[tid].init.append(myPage.getSegment(i), len); + break; + } + case 2: { + myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF); + myMeta.tracks[tid].init += (char)(len & 0xFF); + myMeta.tracks[tid].init.append(myPage.getSegment(i), len); + oggTracks[tid].lastGran = 0; + oggTracks[tid].parsedHeaders = true; + break; + } + } + } + } + + if (myMeta.tracks[tid].codec == "vorbis"){ + for (int i = 0; i < myPage.getAllSegments().size(); i++){ + unsigned long len = myPage.getSegmentLen(i); + vorbis::header tmpHead((char*)myPage.getSegment(i), len); + if (!tmpHead.isHeader()){ + DEBUG_MSG(DLVL_FAIL, "Header read failed!"); + return false; + } + DEBUG_MSG(DLVL_WARN, "Vorbis header, type %d", tmpHead.getHeaderType()); + switch (tmpHead.getHeaderType()){ + //Case 1 is being handled by parseBeginOfStream + case 3: { + //we have the first header stored in contBuffer + myMeta.tracks[tid].init += (char)0x02; + //ID header size + for (int i = 0; i < (oggTracks[tid].contBuffer.size() / 255); i++){ + myMeta.tracks[tid].init += (char)0xFF; + } + myMeta.tracks[tid].init += (char)(oggTracks[tid].contBuffer.size() % 255); + //Comment header size + for (int i = 0; i < (len / 255); i++){ + myMeta.tracks[tid].init += (char)0xFF; + } + myMeta.tracks[tid].init += (char)(len % 255); + myMeta.tracks[tid].init += oggTracks[tid].contBuffer; + oggTracks[tid].contBuffer.clear(); + myMeta.tracks[tid].init.append(myPage.getSegment(i), len); + break; + } + case 5: { + myMeta.tracks[tid].init.append(myPage.getSegment(i), len); + oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels); + oggTracks[tid].parsedHeaders = true; + break; + } + } } } - lastBytePos = ftell(inFile); - DEBUG_MSG(DLVL_WARN,"End of Loop, @ filepos %lld", lastBytePos); } - DEBUG_MSG(DLVL_WARN,"Exited while loop"); + + for (std::map::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){ + fseek(inFile, 0, SEEK_SET); + position tmp = seekFirstData(it->first); + if (tmp.trackID){ + currentPositions.insert(tmp); + } else { + INFO_MSG("missing track: %lu", it->first); + } + } + getNext(); + while (lastPack){ + myMeta.update(lastPack); + getNext(); + } + std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str()); oFile << myMeta.toJSON().toNetPacked(); oFile.close(); + + //myMeta.toPrettyString(std::cout); return true; } - bool inputOGG::seekNextPage(int tid){ - fseek(inFile, oggTracks[tid].lastPageOffset, SEEK_SET); - bool res = true; - do { - res = oggTracks[tid].myPage.read(inFile); - } while(res && snum2tid[oggTracks[tid].myPage.getBitstreamSerialNumber()] != tid); - oggTracks[tid].lastPageOffset = ftell(inFile); - oggTracks[tid].nxtSegment = 0; + position inputOGG::seekFirstData(long long unsigned int tid){ + fseek(inFile, 0, SEEK_SET); + position res; + res.time = 0; + res.trackID = tid; + res.segmentNo = 0; + bool readSuccesfull = true; + bool quitloop = false; + while (!quitloop){ + quitloop = true; + res.bytepos = ftell(inFile); + readSuccesfull = oggTracks[tid].myPage.read(inFile); + if (!readSuccesfull){ + quitloop = true; //break :( + break; + } + if (oggTracks[tid].myPage.getBitstreamSerialNumber() != tid){ + quitloop = false; + continue; + } + if (oggTracks[tid].myPage.getHeaderType() != OGG::Plain){ + quitloop = false; + continue; + } + if (oggTracks[tid].codec == OGG::VORBIS){ + vorbis::header tmpHead((char*)oggTracks[tid].myPage.getSegment(0), oggTracks[tid].myPage.getSegmentLen(0)); + if (tmpHead.isHeader()){ + quitloop = false; + } + } + if (oggTracks[tid].codec == OGG::THEORA){ + theora::header tmpHead((char*)oggTracks[tid].myPage.getSegment(0), oggTracks[tid].myPage.getSegmentLen(0)); + if (tmpHead.isHeader()){ + quitloop = false; + } + } + }// while ( oggTracks[tid].myPage.getHeaderType() != OGG::Plain && readSuccesfull && oggTracks[tid].myPage.getBitstreamSerialNumber() != tid); + INFO_MSG("seek first bytepos: %llu tid: %llu oggTracks[tid].myPage.getHeaderType(): %d ", res.bytepos, tid, oggTracks[tid].myPage.getHeaderType()); + if (!readSuccesfull){ + res.trackID = 0; + } return res; } - void inputOGG::getNext(bool smart) { - if (!sortedSegments.size()){ - for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ - seekNextPage((*it)); - } + void inputOGG::getNext(bool smart){ + if (!currentPositions.size()){ + lastPack.null(); + return; } - if (sortedSegments.size()){ - int tid = (*(sortedSegments.begin())).tid; - bool addedPacket = false; - while (!addedPacket){ - segPart tmpPart; - if (oggTracks[tid].myPage.getSegment(oggTracks[tid].nxtSegment, tmpPart.segData, tmpPart.len)){ - if (oggTracks[tid].nxtSegment == 0 && oggTracks[tid].myPage.getHeaderType() && OGG::Continued){ - segment tmpSeg = *(sortedSegments.begin()); - tmpSeg.parts.push_back(tmpPart); - sortedSegments.erase(sortedSegments.begin()); - sortedSegments.insert(tmpSeg); - }else{ - segment tmpSeg; - tmpSeg.parts.push_back(tmpPart); - tmpSeg.tid = tid; - tmpSeg.time = oggTracks[tid].lastTime; - if (oggTracks[tid].codec == VORBIS) { - std::string data; - data.append(tmpPart.segData, tmpPart.len); - unsigned int blockSize = 0; - Utils::bitstreamLSBF packet; - packet.append(data); - if (!packet.get(1)) { - blockSize = oggTracks[tid].blockSize[oggTracks[tid].vModes[packet.get(vorbis::ilog(oggTracks[tid].vModes.size() - 1))].blockFlag]; - } - oggTracks[tid].lastTime += oggTracks[tid].msPerFrame * (blockSize / oggTracks[tid].channels); - } - if (oggTracks[tid].codec == THEORA) { - oggTracks[tid].lastTime += oggTracks[tid].msPerFrame; - } - sortedSegments.insert(tmpSeg); - addedPacket = true; - } - oggTracks[tid].nxtSegment ++; - }else{ - if (!seekNextPage(tid)){ - break; - } + bool lastCompleteSegment = false; + position curPos = *currentPositions.begin(); + currentPositions.erase(currentPositions.begin()); + segment thisSegment; + thisSegment.tid = curPos.trackID; + thisSegment.time = curPos.time; + thisSegment.bytepos = curPos.bytepos + curPos.segmentNo; + unsigned int oldSegNo = curPos.segmentNo; + fseek(inFile, curPos.bytepos, SEEK_SET); + OGG::Page curPage; + curPage.read(inFile); + thisSegment.parts.push_back(std::string(curPage.getSegment(curPos.segmentNo), curPage.getSegmentLen(curPos.segmentNo))); + bool readFullPacket = false; + if (curPos.segmentNo == curPage.getAllSegments().size() - 1){ + OGG::Page tmpPage; + unsigned int bPos; + while (!readFullPacket){ + bPos = ftell(inFile);//<-- :( + if (!tmpPage.read(inFile)){ + break; } - } - std::string data; + if (tmpPage.getBitstreamSerialNumber() != thisSegment.tid){ + continue; + } + curPos.bytepos = bPos; + curPos.segmentNo = 0; + if (tmpPage.getHeaderType() == OGG::Continued){ + thisSegment.parts.push_back(std::string(tmpPage.getSegment(0), tmpPage.getSegmentLen(0))); + curPos.segmentNo = 1; + if (tmpPage.getAllSegments().size() == 1){ + continue; + } + } else { + lastCompleteSegment = true; //if this segment ends on the page, use granule to sync video time + } + readFullPacket = true; + } + } else { + curPos.segmentNo++; + + // if (oggTracks[thisSegment.tid].codec == OGG::THEORA && curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull) && curPos.segmentNo == curPage.getAllSegments().size() - 1){ //if the next segment is the last one on the page, the (theora) granule should be used to sync the time for the current segment + if ((oggTracks[thisSegment.tid].codec == OGG::THEORA ||oggTracks[thisSegment.tid].codec == OGG::VORBIS)&& curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull) && curPos.segmentNo == curPage.getAllSegments().size() - 1){ //if the next segment is the last one on the page, the (theora) granule should be used to sync the time for the current segment + OGG::Page tmpPage; + while (tmpPage.read(inFile) && tmpPage.getBitstreamSerialNumber() != thisSegment.tid){} + if ((tmpPage.getBitstreamSerialNumber() == thisSegment.tid) && tmpPage.getHeaderType() == OGG::Continued){ + lastCompleteSegment = true; //this segment should be used to sync time using granule + } + } + readFullPacket = true; + } + std::string tmpStr = thisSegment.toJSON(oggTracks[thisSegment.tid].codec).toNetPacked(); + lastPack.reInit(tmpStr.data(), tmpStr.size()); + + if (oggTracks[thisSegment.tid].codec == OGG::VORBIS){ + unsigned long blockSize = 0; + Utils::bitstreamLSBF packet; + packet.append((char*)curPage.getSegment(oldSegNo), curPage.getSegmentLen(oldSegNo)); + if (!packet.get(1)){ + //Read index first + unsigned long vModeIndex = packet.get(vorbis::ilog(oggTracks[thisSegment.tid].vModes.size() - 1)); + blockSize= oggTracks[thisSegment.tid].blockSize[oggTracks[thisSegment.tid].vModes[vModeIndex].blockFlag]; //almost readable. + } else { + DEBUG_MSG(DLVL_WARN, "Packet type != 0"); + } + curPos.time += oggTracks[thisSegment.tid].msPerFrame * (blockSize / oggTracks[thisSegment.tid].channels); + } else if (oggTracks[thisSegment.tid].codec == OGG::THEORA){ + if (lastCompleteSegment == true && curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull)){ //this segment should be used to sync time using granule + long long unsigned int parseGranuleUpper = curPage.getGranulePosition() >> oggTracks[thisSegment.tid].KFGShift ; + long long unsigned int parseGranuleLower(curPage.getGranulePosition() & ((1 << oggTracks[thisSegment.tid].KFGShift) - 1)); + thisSegment.time = oggTracks[thisSegment.tid].msPerFrame * (parseGranuleUpper + parseGranuleLower - 1); + curPos.time = thisSegment.time; + std::string tmpStr = thisSegment.toJSON(oggTracks[thisSegment.tid].codec).toNetPacked(); + lastPack.reInit(tmpStr.data(), tmpStr.size()); + // INFO_MSG("thisTime: %d", lastPack.getTime()); + } + curPos.time += oggTracks[thisSegment.tid].msPerFrame; + } + if (readFullPacket){ + currentPositions.insert(curPos); + } + }//getnext() + + long long unsigned int inputOGG::calcGranuleTime(unsigned long tid, long long unsigned int granule){ + switch (oggTracks[tid].codec){ + case OGG::VORBIS: + return granule * oggTracks[tid].msPerFrame ; //= samples * samples per second + break; + case OGG::THEORA:{ + long long unsigned int parseGranuleUpper = granule >> oggTracks[tid].KFGShift ; + long long unsigned int parseGranuleLower = (granule & ((1 << oggTracks[tid].KFGShift) - 1)); + return (parseGranuleUpper + parseGranuleLower) * oggTracks[tid].msPerFrame ; //= frames * msPerFrame + break; + } + default: + DEBUG_MSG(DLVL_WARN, "Unknown codec, can not calculate time from granule"); + break; + } + return 0; + } + + + void inputOGG::seek(int seekTime){ + currentPositions.clear(); + DEBUG_MSG(DLVL_MEDIUM, "Seeking to %dms", seekTime); + + //for every track + for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + //find first keyframe before keyframe with ms > seektime + position tmpPos; + tmpPos.trackID = *it; + tmpPos.time = myMeta.tracks[*it].keys.begin()->getTime(); + tmpPos.bytepos = myMeta.tracks[*it].keys.begin()->getBpos(); + for (std::deque::iterator ot = myMeta.tracks[*it].keys.begin(); ot != myMeta.tracks[*it].keys.end(); ot++){ + if (ot->getTime() > seekTime){ + break; + } else { + tmpPos.time = ot->getTime(); + tmpPos.bytepos = ot->getBpos(); + } + } + INFO_MSG("Found %dms for track %u at %llu bytepos %llu", seekTime, *it, tmpPos.time, tmpPos.bytepos); + int backChrs=std::min(280ull, tmpPos.bytepos - 1); + fseek(inFile, tmpPos.bytepos - backChrs, SEEK_SET); + char buffer[300]; + fread(buffer, 300, 1, inFile); + char * loc = buffer + backChrs + 2; //start at tmpPos.bytepos+2 + while (loc && !(loc[0] == 'O' && loc[1] == 'g' && loc[2] == 'g' && loc[3] == 'S')){ + loc = (char *)memrchr(buffer, 'O', (loc-buffer) -1 );//seek reverse + } + if (!loc){ + INFO_MSG("Unable to find a page boundary starting @ %llu, track %u", tmpPos.bytepos, *it); + for (int i = 0; i < 300; i++){ + fprintf(stderr, "%0.2X ", buffer[i]); + } + continue; + } + tmpPos.segmentNo = backChrs - (loc - buffer); + tmpPos.bytepos -= tmpPos.segmentNo; + INFO_MSG("Track %u, segment %llu found at bytepos %llu", *it, tmpPos.segmentNo, tmpPos.bytepos); + + currentPositions.insert(tmpPos); } } - void inputOGG::seek(int seekTime) { - DEBUG_MSG(DLVL_WARN,"Seeking is not yet supported for ogg files"); - //Do nothing, seeking is not yet implemented for ogg - } - - void inputOGG::trackSelect(std::string trackSpec) { + void inputOGG::trackSelect(std::string trackSpec){ selectedTracks.clear(); size_t index; - while (trackSpec != "") { + while (trackSpec != ""){ index = trackSpec.find(' '); - selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str())); - DEBUG_MSG(DLVL_WARN, "Added track %d, index = %lu, (index == npos) = %d", atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos); - if (index != std::string::npos) { + selectedTracks.insert(atoll(trackSpec.substr(0, index).c_str())); + if (index != std::string::npos){ trackSpec.erase(0, index + 1); } else { trackSpec = ""; @@ -281,3 +438,9 @@ namespace Mist { } + + + + + + diff --git a/src/input/input_ogg.h b/src/input/input_ogg.h index fbc33e1f..2d9960d6 100644 --- a/src/input/input_ogg.h +++ b/src/input/input_ogg.h @@ -3,27 +3,51 @@ #include namespace Mist { - enum codecType {THEORA, VORBIS}; - struct segPart{ + struct segPart { char * segData; unsigned int len; }; - struct segment{ - bool operator < (const segment & rhs) const { - return time < rhs.time || (time == rhs.time && tid < rhs.tid); - } - std::vector parts; - unsigned int time; - unsigned int tid; + class segment { + public: + segment() : time(0), tid(0), bytepos(0), keyframe(0){} + bool operator < (const segment & rhs) const { + return time < rhs.time || (time == rhs.time && tid < rhs.tid); + } + std::vector parts; + unsigned long long int time; + unsigned long long int tid; + long long unsigned int bytepos; + bool keyframe; + JSON::Value toJSON(OGG::oggCodec myCodec); }; - class oggTrack{ + struct position { + bool operator < (const position & rhs) const { + if (time < rhs.time){ + return true; + } else { + if (time == rhs.time){ + if (trackID < rhs.trackID){ + return true; + } + } + } + return false; + } + long unsigned int trackID; + long long unsigned int time; + long long unsigned int bytepos; + long long unsigned int segmentNo; + }; +/* + class oggTrack { public: - oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0) { } + oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0){ } codecType codec; std::string contBuffer;//buffer for continuing pages + segment myBuffer; double lastTime; long long unsigned int lastGran; bool parsedHeaders; @@ -37,9 +61,10 @@ namespace Mist { //vorbis std::deque vModes; char channels; - long long unsigned int blockSize[2]; - }; - + unsigned long blockSize[2]; + unsigned long getBlockSize(unsigned int vModeIndex); + };*/ + class inputOGG : public Input { public: inputOGG(Util::Config * cfg); @@ -47,19 +72,22 @@ namespace Mist { //Private Functions bool setup(); bool readHeader(); - bool seekNextPage(int tid); + position seekFirstData(long long unsigned int tid); void getNext(bool smart = true); void seek(int seekTime); void trackSelect(std::string trackSpec); void parseBeginOfStream(OGG::Page & bosPage); - + std::set currentPositions; FILE * inFile; - std::map snum2tid; - std::map oggTracks; - std::set sortedSegments; + std::map oggTracks;//this remembers all metadata for every track + std::set sortedSegments;//probably not needing this + long long unsigned int calcGranuleTime(unsigned long tid, long long unsigned int granule); + long long unsigned int calcSegmentDuration(unsigned long tid , std::string & segment); + }; } typedef Mist::inputOGG mistIn; + diff --git a/src/output/output.cpp b/src/output/output.cpp index 15f7f267..61730932 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -163,7 +163,7 @@ namespace Mist { void Output::negotiatePushTracks() { int i = 0; - for (std::map::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){ + for (std::map::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){ negotiateWithBuffer(it->first); i++; } @@ -286,7 +286,7 @@ namespace Mist { } } if (!found){ - for (std::map::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ + for (std::map::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ if (trit->second.codec == (*itc).asStringRef()){ genCounter++; found = true; @@ -326,7 +326,7 @@ namespace Mist { } } if (!found){ - for (std::map::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ + for (std::map::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ if (trit->second.codec == (*itc).asStringRef()){ selectedTracks.insert(trit->first); found = true; @@ -438,8 +438,8 @@ namespace Mist { return; } char id[100]; - sprintf(id, "%s%lu_%d", streamName.c_str(), trackId, pageNum); - curPages[trackId].init(std::string(id),26 * 1024 * 1024); + snprintf(id, 100, "%s%lu_%d", streamName.c_str(), trackId, pageNum); + curPages[trackId].init(id, 26 * 1024 * 1024); if (!(curPages[trackId].mapped)){ DEBUG_MSG(DLVL_FAIL, "Initializing page %s failed", curPages[trackId].name.c_str()); return; @@ -448,7 +448,7 @@ namespace Mist { } /// Prepares all tracks from selectedTracks for seeking to the specified ms position. - void Output::seek(long long pos){ + void Output::seek(unsigned long long pos){ sought = true; firstTime = Util::getMS() - pos; if (!isInitialized){ @@ -457,16 +457,16 @@ namespace Mist { buffer.clear(); currentPacket.null(); updateMeta(); - DEBUG_MSG(DLVL_MEDIUM, "Seeking to %llims", pos); + DEBUG_MSG(DLVL_MEDIUM, "Seeking to %llums", pos); for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ seek(*it, pos); } } - bool Output::seek(int tid, long long pos, bool getNextKey){ + bool Output::seek(unsigned int tid, unsigned long long pos, bool getNextKey){ loadPageForKey(tid, getKeyForTime(tid, pos) + (getNextKey?1:0)); if (!curPages.count(tid) || !curPages[tid].mapped){ - DEBUG_MSG(DLVL_DEVEL, "Aborting seek to %llims in track %d, not available.", pos, tid); + DEBUG_MSG(DLVL_DEVEL, "Aborting seek to %llums in track %u, not available.", pos, tid); return false; } sortedPageInfo tmp; @@ -724,7 +724,7 @@ namespace Mist { } if (trackMap.size()){ for (std::map::iterator it = trackMap.begin(); it != trackMap.end() && tNum < 5; it++){ - int tId = it->second; + unsigned int tId = it->second; char * thisData = playerConn.getData() + (6 * tNum); thisData[0] = ((tId >> 24) & 0xFF); thisData[1] = ((tId >> 16) & 0xFF); @@ -736,7 +736,7 @@ namespace Mist { } }else{ for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end() && tNum < 5; it++){ - int tId = *it; + unsigned int tId = *it; char * thisData = playerConn.getData() + (6 * tNum); thisData[0] = ((tId >> 24) & 0xFF); thisData[1] = ((tId >> 16) & 0xFF); diff --git a/src/output/output.h b/src/output/output.h index fafc6ca7..2e3c96b9 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -20,7 +20,7 @@ namespace Mist { } return (time == rhs.time && tid < rhs.tid); } - int tid; + unsigned int tid; long long unsigned int time; unsigned int offset; }; @@ -53,8 +53,8 @@ namespace Mist { //non-virtual generic functions int run(); void stats(); - void seek(long long pos); - bool seek(int tid, long long pos, bool getNextKey = false); + void seek(unsigned long long pos); + bool seek(unsigned int tid, unsigned long long pos, bool getNextKey = false); void stop(); void setBlocking(bool blocking); long unsigned int getMainSelectedTrack(); diff --git a/src/output/output_hds.cpp b/src/output/output_hds.cpp index fd08f2ae..ead54dda 100644 --- a/src/output/output_hds.cpp +++ b/src/output/output_hds.cpp @@ -12,7 +12,7 @@ namespace Mist { audioTrack = 0; JSON::Value & vidCapa = capa["codecs"][0u][0u]; JSON::Value & audCapa = capa["codecs"][0u][1u]; - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (JSON::ArrIter itb = vidCapa.ArrBegin(); itb != vidCapa.ArrEnd(); itb++){ if (it->second.codec == (*itb).asStringRef()){ videoTracks.insert(it->first); diff --git a/src/output/output_hls.cpp b/src/output/output_hls.cpp index cd40bd67..b8e07825 100644 --- a/src/output/output_hls.cpp +++ b/src/output/output_hls.cpp @@ -10,14 +10,14 @@ namespace Mist { result << "#EXTM3U\r\n"; int audioId = -1; std::string audioName; - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ if (it->second.codec == "AAC"){ audioId = it->first; audioName = it->second.getIdentifier(); break; } } - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ if (it->second.codec == "H264"){ int bWidth = it->second.bps * 2; if (audioId != -1){ @@ -266,7 +266,7 @@ namespace Mist { return 1; } //loop trough all the tracks - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ //return "too late" if one track is past this point if (ms < it->second.firstms){ return -1; diff --git a/src/output/output_hss.cpp b/src/output/output_hss.cpp index 4fd889cb..6629ef15 100644 --- a/src/output/output_hss.cpp +++ b/src/output/output_hss.cpp @@ -295,13 +295,13 @@ namespace Mist { "MajorVersion=\"2\" " "MinorVersion=\"0\" " "TimeScale=\"10000000\" "; - std::deque::iterator> audioIters; - std::deque::iterator> videoIters; + std::deque::iterator> audioIters; + std::deque::iterator> videoIters; long long int maxWidth = 0; long long int maxHeight = 0; long long int minWidth = 99999999; long long int minHeight = 99999999; - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { if (it->second.codec == "AAC") { audioIters.push_back(it); } @@ -343,7 +343,7 @@ namespace Mist { "Chunks=\"" << (*audioIters.begin())->second.keys.size() << "\" " "Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n"; int index = 0; - for (std::deque::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) { + for (std::deque::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) { Result << "second.bps * 8 << "\" " @@ -388,7 +388,7 @@ namespace Mist { "DisplayWidth=\"" << maxWidth << "\" " "DisplayHeight=\"" << maxHeight << "\">\n"; int index = 0; - for (std::deque::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) { + for (std::deque::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) { //Add video qualities Result << "::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { for (std::deque::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++) { keyTimes[it->first].insert(it2->getTime()); } diff --git a/src/output/output_json.cpp b/src/output/output_json.cpp index 35a69ceb..0a455f33 100644 --- a/src/output/output_json.cpp +++ b/src/output/output_json.cpp @@ -51,7 +51,7 @@ namespace Mist { if (H.GetVar("callback") != ""){jsonp = H.GetVar("callback");} if (H.GetVar("jsonp") != ""){jsonp = H.GetVar("jsonp");} initialize(); - for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ if (it->second.type == "meta" ){ selectedTracks.insert(it->first); } diff --git a/src/output/output_progressive_ogg.cpp b/src/output/output_progressive_ogg.cpp new file mode 100644 index 00000000..7ec90728 --- /dev/null +++ b/src/output/output_progressive_ogg.cpp @@ -0,0 +1,208 @@ +#include "output_progressive_ogg.h" +#include +#include +#include + +namespace Mist { + OutProgressiveOGG::OutProgressiveOGG(Socket::Connection & conn) : HTTPOutput(conn){ + realTime = 0; + } + + OutProgressiveOGG::~OutProgressiveOGG(){} + + void OutProgressiveOGG::init(Util::Config * cfg){ + HTTPOutput::init(cfg); + capa["name"] = "OGG"; + capa["desc"] = "Enables HTTP protocol progressive streaming."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/$.ogg"; + capa["url_match"] = "/$.ogg"; + capa["codecs"][0u][0u].append("theora"); + capa["codecs"][0u][1u].append("vorbis"); + capa["codecs"][0u][1u].append("opus"); + capa["methods"][0u]["handler"] = "http"; + capa["methods"][0u]["type"] = "html5/video/ogg"; + capa["methods"][0u]["priority"] = 8ll; + capa["methods"][0u]["nolive"] = 1; + } + + void OutProgressiveOGG::sendNext(){ + unsigned int track = currentPacket.getTrackId(); + + + OGG::oggSegment newSegment; + currentPacket.getString("data", newSegment.dataString); + // if (currentPacket.getTime() > 315800){// && currentPacket.getTime() < 316200){ + //INFO_MSG("Found a packet of time %llu, size: %d", currentPacket.getTime(), newSegment.dataString.size()); + //} + pageBuffer[track].totalFrames = ((double)currentPacket.getTime() / (1000000.0f / myMeta.tracks[track].fpks)) + 1.5; //should start at 1. added .5 for rounding. +// INFO_MSG("track: %u totalFrames %llu timestamp: %llu totalframe value: %f", track, pageBuffer[track].totalFrames, currentPacket.getTime(), ((double)currentPacket.getTime() / (1000000.0f / myMeta.tracks[track].fpks)) + 1); + + if (pageBuffer[track].codec == OGG::THEORA){ + newSegment.isKeyframe = currentPacket.getFlag("keyframe"); + if (newSegment.isKeyframe == true){ + pageBuffer[track].sendTo(myConn);//send data remaining in buffer (expected to fit on a page), keyframe will allways start on new page + // INFO_MSG("segments left in buffer: %d", pageBuffer[track].oggSegments.size()); + pageBuffer[track].lastKeyFrame = pageBuffer[track].totalFrames; + } + newSegment.framesSinceKeyFrame = pageBuffer[track].totalFrames - pageBuffer[track].lastKeyFrame; + newSegment.lastKeyFrameSeen = pageBuffer[track].lastKeyFrame; +// theora::frame tmpFrame; +// tmpFrame.read(newSegment.dataString.data(),newSegment.dataString.size()); +// INFO_MSG("FTYPE: %d ISKEYFRAME: %d",tmpFrame.getFTYPE(),newSegment.isKeyframe ); + } + + newSegment.frameNumber = pageBuffer[track].totalFrames; + newSegment.timeStamp = currentPacket.getTime(); + + pageBuffer[track].oggSegments.push_back(newSegment); + + if (pageBuffer[track].codec == OGG::VORBIS){ + pageBuffer[track].vorbisStuff();//this updates lastKeyFrame + } + + // while (pageBuffer[track].oggSegments.size()){ + //pageBuffer[track].sendTo(myConn); + //} + while (pageBuffer[track].shouldSend()){ + pageBuffer[track].sendTo(myConn); + } + } + + bool OutProgressiveOGG::onFinish(){ + for (std::map::iterator it = pageBuffer.begin(); it != pageBuffer.end(); it++){ + it->second.setHeaderType(OGG::EndOfStream); + it->second.sendTo(myConn); + } + return false; + } + bool OutProgressiveOGG::parseInit(std::string & initData, std::deque & output){ + std::string temp; + unsigned int index = 0; + if (initData[0] == 0x02){ //"special" case, requires interpretation similar to table + if (initData.size() < 7){ + FAIL_MSG("initData size too tiny (size: %lu)", initData.size()); + return false; + } + unsigned int len1 = 0 ; + unsigned int len2 = 0 ; + index = 1; + while (initData[index] == 255){ //get len 1 + len1 += initData[index++]; + } + len1 += initData[index++]; + + while (initData[index] == 255){ //get len 1 + len2 += initData[index++]; + } + len2 += initData[index++]; + + if (initData.size() < (len1 + len2 + 4)){ + FAIL_MSG("initData size too tiny (size: %lu)", initData.size()); + return false; + } + + temp = initData.substr(index, len1); + output.push_back(temp); + index += len1; + temp = initData.substr(index, len2); + output.push_back(temp); + index += len2; + temp = initData.substr(index); //remainder of string: + output.push_back(temp); //add data to output deque + } else { + if (initData.size() < 7){ + FAIL_MSG("initData size too tiny (size: %lu)", initData.size()); + return false; + } + unsigned int len = 0; + for (unsigned int i = 0; i < 3; i++){ + temp = initData.substr(index, 2); + len = (((unsigned int)temp[0]) << 8) | (temp[1]); //2 bytes len + index += 2; //start of data + if (index + len > initData.size()){ + FAIL_MSG("index+len > initData size"); + return false; + } + temp = initData.substr(index, len); + output.push_back(temp); //add data to output deque + index += len; + INFO_MSG("init data len[%d]: %d ", i, len); + } + } + + return true; + } + + void OutProgressiveOGG::sendHeader(){ + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetHeader("Content-Type", "video/ogg"); + HTTP_S.protocol = "HTTP/1.0"; + myConn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file + + std::map > initData; + + OGG::oggSegment newSegment; + for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + parseInit(myMeta.tracks[*it].init, initData[*it]); + if (myMeta.tracks[*it].codec == "theora"){ //get size and position of init data for this page. + pageBuffer[*it].codec = OGG::THEORA; + pageBuffer[*it].totalFrames = 1; //starts at frame number 1, according to weird offDetectMeta function. + std::string tempStr = initData[*it][0]; + theora::header tempHead((char *)tempStr.c_str(), 42); + pageBuffer[*it].split = tempHead.getKFGShift(); + INFO_MSG("got theora KFG shift: %d", pageBuffer[*it].split); //looks OK. + } else if (myMeta.tracks[*it].codec == "vorbis"){ + pageBuffer[*it].codec = OGG::VORBIS; + pageBuffer[*it].totalFrames = 0; + pageBuffer[*it].sampleRate = myMeta.tracks[*it].rate; + pageBuffer[*it].prevBlockFlag = -1; + vorbis::header tempHead((char *)initData[*it][0].data(), initData[*it][0].size()); + pageBuffer[*it].blockSize[0] = std::min(tempHead.getBlockSize0(), tempHead.getBlockSize1()); + pageBuffer[*it].blockSize[1] = std::max(tempHead.getBlockSize0(), tempHead.getBlockSize1()); + char audioChannels = tempHead.getAudioChannels(); //? + vorbis::header tempHead2((char *)initData[*it][2].data(), initData[*it][2].size()); + pageBuffer[*it].vorbisModes = tempHead2.readModeDeque(audioChannels);//getting modes + } else if (myMeta.tracks[*it].codec == "opus"){ + pageBuffer[*it].totalFrames = 0; //? + pageBuffer[*it].codec = OGG::OPUS; + } + pageBuffer[*it].clear(OGG::BeginOfStream, 0, *it, 0); //CREATES a (map)pageBuffer object, *it = id, pagetype=BOS + newSegment.dataString = initData[*it][0]; + pageBuffer[*it].oggSegments.push_back(newSegment); + pageBuffer[*it].sendTo(myConn, 0); //granule position of 0 + } + for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + newSegment.dataString = initData[*it][1]; + pageBuffer[*it].oggSegments.push_back(newSegment); + newSegment.dataString = initData[*it][2]; + pageBuffer[*it].oggSegments.push_back(newSegment); + while (pageBuffer[*it].oggSegments.size()){ + pageBuffer[*it].sendTo(myConn, 0); //granule position of 0 + } + } + sentHeader = true; + } + + void OutProgressiveOGG::onRequest(){ + if (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request %s", HTTP_R.getUrl().c_str()); + if (HTTP_R.GetVar("audio") != ""){ + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("audio")).asInt()); + } + if (HTTP_R.GetVar("video") != ""){ + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("video")).asInt()); + } + parseData = true; + wantRequest = false; + HTTP_R.Clean(); + } + } +} + + + + + + + diff --git a/src/output/output_progressive_ogg.h b/src/output/output_progressive_ogg.h new file mode 100644 index 00000000..84a7b63f --- /dev/null +++ b/src/output/output_progressive_ogg.h @@ -0,0 +1,28 @@ +#include "output_http.h" +#include +#include + +namespace Mist { + class OutProgressiveOGG : public HTTPOutput { + public: + OutProgressiveOGG(Socket::Connection & conn); + ~OutProgressiveOGG(); + static void init(Util::Config * cfg); + void onRequest(); + void sendNext(); + void sendHeader(); + bool onFinish(); + bool parseInit(std::string & initData, std::deque & output); + protected: + HTTP::Parser HTTP_R;//Received HTTP + HTTP::Parser HTTP_S;//Sent HTTP + std::map pageBuffer; //OGG specific variables + }; +} + +typedef Mist::OutProgressiveOGG mistOut; + + + + +