Ogg support fixed and re-added. Squash of various commits made by Wouter Spruit.

This commit is contained in:
Thulinma 2014-12-23 13:10:28 +01:00
parent 142ef73f6c
commit a47504b5cb
17 changed files with 944 additions and 456 deletions

View file

@ -57,9 +57,9 @@ analysers: MistAnalyserMP4
MistAnalyserMP4: src/analysers/mp4_analyser.cpp MistAnalyserMP4: src/analysers/mp4_analyser.cpp
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
#analysers: MistAnalyserOGG analysers: MistAnalyserOGG
#MistAnalyserOGG: src/analysers/ogg_analyser.cpp MistAnalyserOGG: src/analysers/ogg_analyser.cpp
# $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
analysers: MistInfo analysers: MistInfo
MistInfo: src/analysers/info.cpp 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 MistInFLV: src/input/mist_in.cpp src/input/input.cpp src/input/input_flv.cpp
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
#inputs: MistInOGG inputs: MistInOGG
#MistInOGG: override LDLIBS += $(THREADLIB) MistInOGG: override LDLIBS += $(THREADLIB)
#MistInOGG: override CPPFLAGS += "-DINPUTTYPE=\"input_ogg.h\"" MistInOGG: override CPPFLAGS += "-DINPUTTYPE=\"input_ogg.h\""
#MistInOGG: src/input/mist_in.cpp src/input/input.cpp src/input/input_ogg.cpp MistInOGG: src/input/mist_in.cpp src/input/input.cpp src/input/input_ogg.cpp
# $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
inputs: MistInBuffer inputs: MistInBuffer
MistInBuffer: override LDLIBS += $(THREADLIB) 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 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 $@ $(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 outputs: MistOutMP4
MistOutMP4: override LDLIBS += $(THREADLIB) MistOutMP4: override LDLIBS += $(THREADLIB)
MistOutMP4: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp4.h\"" MistOutMP4: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp4.h\""

View file

@ -35,7 +35,7 @@ namespace Analysers {
break; break;
} }
case DTSC::DTSC_V2: { 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; break;
} }
case DTSC::DTSC_HEAD: { case DTSC::DTSC_HEAD: {

View file

@ -1,5 +1,5 @@
#include <cstdlib> #include <cstdlib>
#include <cstdio> #include <stdio.h>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <string> #include <string>
@ -8,48 +8,90 @@
#include <mist/ogg.h> #include <mist/ogg.h>
#include <mist/config.h> #include <mist/config.h>
#include <mist/theora.h> #include <mist/theora.h>
///\todo rewrite this analyser.
namespace Analysers{ namespace Analysers {
std::string Opus_prettyPacket(char * part,int len){ std::string Opus_prettyPacket(const char * part, int len){
if (len < 1){ if (len < 1){
return "Invalid packet (0 byte length)"; return "Invalid packet (0 byte length)";
} }
std::stringstream r; std::stringstream r;
char config = part[0] >> 3; char config = part[0] >> 3;
char code = 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){ if (config < 14){
r << "SILK, "; r << "SILK, ";
if (config < 4){r << "NB, ";} if (config < 4){
if (config < 8 && config > 3){r << "MB, ";} r << "NB, ";
if (config < 14 && config > 7){r << "WB, ";} }
if (config % 4 == 0){r << "10ms";} if (config < 8 && config > 3){
if (config % 4 == 1){r << "20ms";} r << "MB, ";
if (config % 4 == 2){r << "40ms";} }
if (config % 4 == 3){r << "60ms";} 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){ if (config < 16 && config > 13){
r << "Hybrid, "; r << "Hybrid, ";
if (config < 14){r << "SWB, ";}else{r << "FB, ";} if (config < 14){
if (config % 2 == 0){r << "10ms";}else{r << "20ms";} r << "SWB, ";
} else {
r << "FB, ";
}
if (config % 2 == 0){
r << "10ms";
} else {
r << "20ms";
}
} }
if (config > 15){ if (config > 15){
r << "CELT, "; r << "CELT, ";
if (config < 20){r << "NB, ";} if (config < 20){
if (config < 24 && config > 19){r << "WB, ";} r << "NB, ";
if (config < 28 && config > 23){r << "SWB, ";} }
if (config > 27){r << "FB, ";} if (config < 24 && config > 19){
if (config % 4 == 0){r << "2.5ms";} r << "WB, ";
if (config % 4 == 1){r << "5ms";} }
if (config % 4 == 2){r << "10ms";} if (config < 28 && config > 23){
if (config % 4 == 3){r << "20ms";} 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){ if (code == 0){
r << ": 1 packet (" << (len-1) << "b)"; r << ": 1 packet (" << (len - 1) << "b)";
return r.str(); return r.str();
} }
if (code == 1){ 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(); return r.str();
} }
if (code == 2){ if (code == 2){
@ -57,10 +99,10 @@ namespace Analysers{
return "Invalid packet (code 2 must be > 1 byte long)"; return "Invalid packet (code 2 must be > 1 byte long)";
} }
if (part[1] < 252){ if (part[1] < 252){
r << ": 2 packets (" << (int)part[1] << "b / " << (int)(len-2-part[1]) << "b)"; r << ": 2 packets (" << (int)part[1] << "b / " << (int)(len - 2 - part[1]) << "b)";
}else{ } else {
int ilen = part[1] + part[2]*4; int ilen = part[1] + part[2] * 4;
r << ": 2 packets (" << ilen << "b / " << (int)(len-3-ilen) << "b)"; r << ": 2 packets (" << ilen << "b / " << (int)(len - 3 - ilen) << "b)";
} }
return r.str(); return r.str();
} }
@ -71,112 +113,115 @@ namespace Analysers{
r << ": " << packets << " packets (VBR = " << VBR << ", padding = " << pad << ")"; r << ": " << packets << " packets (VBR = " << VBR << ", padding = " << pad << ")";
return r.str(); return r.str();
} }
int analyseOGG(int argc, char ** argv){ int analyseOGG(int argc, char ** argv){
Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); 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.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); conf.parseArgs(argc, argv);
std::map<int,std::string> sn2Codec; std::map<int, std::string> sn2Codec;
std::string oggBuffer; std::string oggBuffer;
OGG::Page oggPage; OGG::Page oggPage;
int kfgshift;
//Read all of std::cin to oggBuffer //Read all of std::cin to oggBuffer
//while stream busy //while OGG::page check function read
while (std::cin.good()){ while (oggPage.read(stdin)){ //reading ogg to string
for (unsigned int i = 0; (i < 1024) && (std::cin.good()); i++){ //print the Ogg page details, if requested
oggBuffer += std::cin.get(); if (conf.getBool("pages")){
printf("%s", oggPage.toPrettyString().c_str());
} }
//while OGG::page check function read
while (oggPage.read(oggBuffer)){//reading ogg to string //attempt to detect codec if this is the first page of a stream
//print the Ogg page details, if requested if (oggPage.getHeaderType() & OGG::BeginOfStream){
if (conf.getBool("pages")){ if (memcmp("theora", oggPage.getSegment(0) + 1, 6) == 0){
std::cout << oggPage.toPrettyString() << std::endl; sn2Codec[oggPage.getBitstreamSerialNumber()] = "Theora";
} }
if (memcmp("vorbis", oggPage.getSegment(0) + 1, 6) == 0){
//attempt to detect codec if this is the first page of a stream sn2Codec[oggPage.getBitstreamSerialNumber()] = "Vorbis";
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("OpusHead", oggPage.getSegment(0), 8) == 0){
if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Theora"){ sn2Codec[oggPage.getBitstreamSerialNumber()] = "Opus";
std::cout << "Theora data" << std::endl; }
int offset = 0; if (sn2Codec[oggPage.getBitstreamSerialNumber()] != ""){
for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ std::cout << "Bitstream " << oggPage.getBitstreamSerialNumber() << " recognized as " << sn2Codec[oggPage.getBitstreamSerialNumber()] << std::endl;
theora::header tmpHeader; } else {
int len = oggPage.getSegmentTableDeque()[i]; std::cout << "Bitstream " << oggPage.getBitstreamSerialNumber() << " could not be recognized as any known codec" << std::endl;
if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){ }
std::cout << tmpHeader.toPrettyString(2);
}
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; } else {
if (tmpFrame.read(oggPage.getFullPayload()+offset,len)){ if (!(oggPage.getHeaderType() == OGG::Continued) && tmpHeader.getFTYPE() == 0){ //if keyframe
std::cout << tmpFrame.toPrettyString(2); 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 << tmpHeader.toPrettyString(4);
std::cout << "Vorbis data" << std::endl;
int offset = 0; }
for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ } else if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Vorbis"){
vorbis::header tmpHeader; std::cout << " Vorbis data" << std::endl;
int len = oggPage.getSegmentTableDeque()[i]; for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){ int len = oggPage.getAllSegments()[i].size();
std::cout << tmpHeader.toPrettyString(2); vorbis::header tmpHeader((char*)oggPage.getSegment(i), len);
} if (tmpHeader.isHeader()){
offset += len; std::cout << tmpHeader.toPrettyString(4);
} }
}else if(sn2Codec[oggPage.getBitstreamSerialNumber()] == "Opus"){ }
std::cout << "Opus data" << std::endl; } else if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Opus"){
int offset = 0; std::cout << " Opus data" << std::endl;
for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ int offset = 0;
int len = oggPage.getSegmentTableDeque()[i]; for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
char * part = oggPage.getFullPayload() + offset; int len = oggPage.getAllSegments()[i].size();
if (len >= 8 && memcmp(part, "Opus", 4) == 0){ const char * part = oggPage.getSegment(i);
if (memcmp(part, "OpusHead", 8) == 0){ if (len >= 8 && memcmp(part, "Opus", 4) == 0){
std::cout << " Version: " << (int)(part[8]) << std::endl; if (memcmp(part, "OpusHead", 8) == 0){
std::cout << " Channels: " << (int)(part[9]) << std::endl; std::cout << " Version: " << (int)(part[8]) << std::endl;
std::cout << " Pre-skip: " << (int)(part[10] + (part[11] << 8)) << std::endl; std::cout << " Channels: " << (int)(part[9]) << std::endl;
std::cout << " Orig. sample rate: " << (int)(part[12] + (part[13] << 8) + (part[14] << 16) + (part[15] << 24)) << std::endl; std::cout << " Pre-skip: " << (int)(part[10] + (part[11] << 8)) << std::endl;
std::cout << " Gain: " << (int)(part[16] + (part[17] << 8)) << std::endl; std::cout << " Orig. sample rate: " << (int)(part[12] + (part[13] << 8) + (part[14] << 16) + (part[15] << 24)) << std::endl;
std::cout << " Channel map: " << (int)(part[18]) << std::endl; std::cout << " Gain: " << (int)(part[16] + (part[17] << 8)) << std::endl;
if (part[18] > 0){ std::cout << " Channel map: " << (int)(part[18]) << std::endl;
std::cout << " Channel map family " << (int)(part[18]) << " not implemented - output incomplete" << 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); if (memcmp(part, "OpusTags", 8) == 0){
std::cout << " Vendor: " << std::string(part+12, vendor_len) << std::endl; unsigned int vendor_len = part[8] + (part[9] << 8) + (part[10] << 16) + (part[11] << 24);
char * str_data = part+12+vendor_len; std::cout << " Vendor: " << std::string(part + 12, vendor_len) << std::endl;
unsigned int strings = str_data[0] + (str_data[1]<<8) + (str_data[2]<<16) + (str_data[3]<<24); const char * str_data = part + 12 + vendor_len;
std::cout << " Tags: (" << strings << ")" << std::endl; 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; str_data += 4;
for (unsigned int j = 0; j < strings; j++){ std::cout << " [" << j << "] " << std::string((char *) str_data, strlen) << std::endl;
unsigned int strlen = str_data[0] + (str_data[1]<<8) + (str_data[2]<<16) + (str_data[3]<<24); str_data += strlen;
str_data += 4;
std::cout << " [" << j << "] " << std::string(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; return 0;
} }
@ -186,3 +231,4 @@ int main(int argc, char ** argv){
return Analysers::analyseOGG(argc, argv); return Analysers::analyseOGG(argc, argv);
} }

View file

@ -110,7 +110,7 @@ namespace Controller {
data["online"] = 0; data["online"] = 0;
return; return;
}else{ }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()); 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; getMeta = true;
} }

View file

@ -13,9 +13,9 @@ namespace Mist {
void Input::userCallback(char * data, size_t len, unsigned int id){ void Input::userCallback(char * data, size_t len, unsigned int id){
for (int i = 0; i < 5; i++){ 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){ 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 bufferFrame(tid, keyNum + 1);//Try buffer next frame
} }
} }
@ -155,7 +155,7 @@ namespace Mist {
if (!isBuffer){ if (!isBuffer){
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
bufferFrame(it->first, 1); bufferFrame(it->first, 1);
} }
} }
@ -204,8 +204,8 @@ namespace Mist {
DEBUG_MSG(DLVL_DONTEVEN,"Parsing the header"); DEBUG_MSG(DLVL_DONTEVEN,"Parsing the header");
selectedTracks.clear(); selectedTracks.clear();
std::stringstream trackSpec; std::stringstream trackSpec;
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first); DEBUG_MSG(DLVL_VERYHIGH, "Track %u encountered", it->first);
if (trackSpec.str() != ""){ if (trackSpec.str() != ""){
trackSpec << " "; trackSpec << " ";
} }
@ -218,16 +218,18 @@ namespace Mist {
trackSelect(trackSpec.str()); trackSelect(trackSpec.str());
bool hasKeySizes = true; bool hasKeySizes = true;
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (!it->second.keySizes.size()){ if (!it->second.keySizes.size()){
hasKeySizes = false; hasKeySizes = false;
break; break;
} }
} }
INFO_MSG("%s", (hasKeySizes ? "hasKeysizes" : "noHasKeysizes"));
if (hasKeySizes){ if (hasKeySizes){
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
char tmpId[20]; 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. indexPages[it->first].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
bool newData = true; bool newData = true;
for (int i = 0; i < it->second.keys.size(); i++){ for (int i = 0; i < it->second.keys.size(); i++){
@ -245,68 +247,72 @@ namespace Mist {
} }
} }
}else{ }else{
std::map<int, DTSCPageData> curData; std::map<int, DTSCPageData> curData;
std::map<int, booking> bookKeeping; std::map<int, booking> bookKeeping;
seek(0); seek(0);
getNext(); getNext();
while(lastPack){//loop through all while(lastPack){//loop through all
int tid = lastPack.getTrackId(); unsigned int tid = lastPack.getTrackId();
if (!tid){ 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);
getNext(false); getNext(false);
continue;
} }
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ if (!bookKeeping.count(tid)){
if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){ bookKeeping[tid].first = 1;
pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first]; 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<unsigned int, DTSC::Track>::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<int, DTSC::Track>::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<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){ if (!pagesByTrack.count(it->first)){
DEBUG_MSG(DLVL_VERYHIGH, "Page %u-%u, (%llu bytes)", it2->first, it2->first + it2->second.keyNum - 1, it2->second.dataSize); 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<int, DTSCPageData>::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 (keyNum < 1){keyNum = 1;}
if (!pagesByTrack.count(track)){ if (!pagesByTrack.count(track)){
return false; return false;
@ -315,21 +321,22 @@ namespace Mist {
if (it != pagesByTrack[track].begin()){ if (it != pagesByTrack[track].begin()){
it--; it--;
} }
int pageNum = it->first; unsigned int pageNum = it->first;
pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use 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)){ if (dataPages[track].count(pageNum)){
return true; return true;
} }
char pageId[100]; 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); std::string tmpString(pageId, pageIdLen);
#ifdef __CYGWIN__ #ifdef __CYGWIN__
dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true); dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true);
#else #else
dataPages[track][pageNum].init(tmpString, it->second.dataSize, true); dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
#endif #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; std::stringstream trackSpec;
trackSpec << track; 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); 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; break;
}else{ }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()); memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
it->second.curOffset += lastPack.getDataLen(); it->second.curOffset += lastPack.getDataLen();
} }
@ -365,7 +373,7 @@ namespace Mist {
break; 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; return true;
} }

View file

@ -45,7 +45,7 @@ namespace Mist {
virtual void userCallback(char * data, size_t len, unsigned int id); virtual void userCallback(char * data, size_t len, unsigned int id);
void parseHeader(); 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. unsigned int packTime;///Media-timestamp of the last packet.
int lastActive;///Timestamp of the last time we received or sent something. int lastActive;///Timestamp of the last time we received or sent something.
@ -53,7 +53,7 @@ namespace Mist {
int playing; int playing;
unsigned int playUntil; unsigned int playUntil;
unsigned int benchMark; unsigned int benchMark;
std::set<int> selectedTracks; std::set<unsigned int> selectedTracks;
bool isBuffer; bool isBuffer;

View file

@ -43,7 +43,7 @@ namespace Mist {
inputBuffer::~inputBuffer(){ inputBuffer::~inputBuffer(){
if (myMeta.tracks.size()){ if (myMeta.tracks.size()){
DEBUG_MSG(DLVL_DEVEL, "Cleaning up, removing last keyframes"); DEBUG_MSG(DLVL_DEVEL, "Cleaning up, removing last keyframes");
for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for(std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
while (removeKey(it->first)){} while (removeKey(it->first)){}
} }
} }
@ -52,7 +52,7 @@ namespace Mist {
void inputBuffer::updateMeta(){ void inputBuffer::updateMeta(){
long long unsigned int firstms = 0xFFFFFFFFFFFFFFFFull; long long unsigned int firstms = 0xFFFFFFFFFFFFFFFFull;
long long unsigned int lastms = 0; long long unsigned int lastms = 0;
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.firstms < firstms){ if (it->second.firstms < firstms){
firstms = it->second.firstms; firstms = it->second.firstms;
} }
@ -116,14 +116,14 @@ namespace Mist {
void inputBuffer::removeUnused(){ void inputBuffer::removeUnused(){
//find the earliest video keyframe stored //find the earliest video keyframe stored
unsigned int firstVideo = 1; unsigned int firstVideo = 1;
for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for(std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.type == "video"){ if (it->second.type == "video"){
if (it->second.firstms < firstVideo || firstVideo == 1){ if (it->second.firstms < firstVideo || firstVideo == 1){
firstVideo = it->second.firstms; firstVideo = it->second.firstms;
} }
} }
} }
for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for(std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
//non-video tracks need to have a second keyframe that is <= firstVideo //non-video tracks need to have a second keyframe that is <= firstVideo
if (it->second.type != "video"){ if (it->second.type != "video"){
if (it->second.keys.size() < 2 || it->second.keys[1].getTime() > firstVideo){ if (it->second.keys.size() < 2 || it->second.keys[1].getTime() > firstVideo){

View file

@ -9,269 +9,426 @@
#include <mist/ogg.h> #include <mist/ogg.h>
#include <mist/defines.h> #include <mist/defines.h>
#include <mist/bitstream.h> #include <mist/bitstream.h>
#include "input_ogg.h" #include "input_ogg.h"
///\todo Whar be Opus support?
namespace Mist { 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["name"] = "OGG";
capa["decs"] = "Enables OGG Input"; capa["desc"] = "Enables OGG input";
capa["codecs"][0u][0u].append("theora"); capa["codecs"][0u][0u].append("theora");
capa["codecs"][0u][1u].append("vorbis"); capa["codecs"][0u][1u].append("vorbis");
} }
bool inputOGG::setup() { bool inputOGG::setup(){
if (config->getString("input") == "-") { if (config->getString("input") == "-"){
std::cerr << "Input from stdin not yet supported" << std::endl; std::cerr << "Input from stream not yet supported" << std::endl;
return false; 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 //open File
inFile = fopen(config->getString("input").c_str(), "r"); inFile = fopen(config->getString("input").c_str(), "r");
if (!inFile) { if (!inFile){
return false; return false;
} }
return true; return true;
} }
void inputOGG::parseBeginOfStream(OGG::Page & bosPage) { ///\todo check if all trackID (tid) instances are replaced with bitstream serial numbers
long long int tid = snum2tid.size() + 1; void inputOGG::parseBeginOfStream(OGG::Page & bosPage){
snum2tid[bosPage.getBitstreamSerialNumber()] = tid; //long long int tid = snum2tid.size() + 1;
if (!memcmp(bosPage.getFullPayload() + 1, "theora", 6)) { unsigned int tid = bosPage.getBitstreamSerialNumber();
oggTracks[tid].codec = THEORA; if (memcmp(bosPage.getSegment(0) + 1, "theora", 6) == 0){
theora::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize()); theora::header tmpHead((char*)bosPage.getSegment(0), bosPage.getSegmentLen(0));
oggTracks[tid].msPerFrame = (double)(tmpHead.getFRD() * 1000) / tmpHead.getFRN(); 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)) { if (memcmp(bosPage.getSegment(0) + 1, "vorbis", 6) == 0){
oggTracks[tid].codec = VORBIS; vorbis::header tmpHead((char*)bosPage.getSegment(0), bosPage.getSegmentLen(0));
vorbis::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize());
oggTracks[tid].msPerFrame = (double)1000 / ntohl(tmpHead.getAudioSampleRate()); 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() { bool inputOGG::readHeader(){
JSON::Value lastPack; OGG::Page myPage;
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
fseek(inFile, 0, SEEK_SET); fseek(inFile, 0, SEEK_SET);
OGG::Page tmpPage; while (myPage.read(inFile)){ //assumes all headers are sent before any data
long long int lastBytePos = 0; unsigned int tid = myPage.getBitstreamSerialNumber();
while (tmpPage.read(inFile)) { if (myPage.getHeaderType() & OGG::BeginOfStream){
DEBUG_MSG(DLVL_WARN,"Read a page"); parseBeginOfStream(myPage);
if (tmpPage.getHeaderType() & OGG::BeginOfStream){ INFO_MSG("Read BeginOfStream for track %d", tid);
parseBeginOfStream(tmpPage); continue; //Continue reading next pages
DEBUG_MSG(DLVL_WARN,"Read BOS page for stream %lu, now track %lld", tmpPage.getBitstreamSerialNumber(), snum2tid[tmpPage.getBitstreamSerialNumber()]);
} }
int offset = 0;
long long int tid = snum2tid[tmpPage.getBitstreamSerialNumber()]; bool readAllHeaders = true;
for (std::deque<unsigned int>::iterator it = tmpPage.getSegmentTableDeque().begin(); it != tmpPage.getSegmentTableDeque().end(); it++) { for (std::map<long unsigned int, OGG::oggTrack>::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){
if (oggTracks[tid].parsedHeaders) { if (!it->second.parsedHeaders){
DEBUG_MSG(DLVL_WARN,"Parsing a page segment on track %lld", tid); readAllHeaders = false;
if ((it == (tmpPage.getSegmentTableDeque().end() - 1)) && (int)(tmpPage.getPageSegments()) == 255 && (int)(tmpPage.getSegmentTable()[254]) == 255) { break;
oggTracks[tid].contBuffer.append(tmpPage.getFullPayload() + offset, (*it)); }
} else { }
lastPack["trackid"] = tid; if (readAllHeaders){
lastPack["time"] = (long long)oggTracks[tid].lastTime; break;
if (oggTracks[tid].contBuffer.size()) { }
lastPack["data"] = oggTracks[tid].contBuffer + std::string(tmpPage.getFullPayload() + offset, (*it));
oggTracks[tid].contBuffer.clear(); // INFO_MSG("tid: %d",tid);
} else {
lastPack["data"] = std::string(tmpPage.getFullPayload() + offset, (*it)); //Parsing headers
} // INFO_MSG("parsing headers for tid: %d",tid);
if (oggTracks[tid].codec == VORBIS) { ///\todo make sure the header is ffmpeg compatible
unsigned int blockSize = 0; if (myMeta.tracks[tid].codec == "theora"){
Utils::bitstreamLSBF packet; // INFO_MSG("theora");
packet.append(lastPack["data"].asString()); for (int i = 0; i < myPage.getAllSegments().size(); i++){
if (!packet.get(1)) { unsigned long len = myPage.getSegmentLen(i);
blockSize = oggTracks[tid].blockSize[oggTracks[tid].vModes[packet.get(vorbis::ilog(oggTracks[tid].vModes.size() - 1))].blockFlag]; INFO_MSG("Length for segment %d: %lu", i, len);
} else { theora::header tmpHead((char*)myPage.getSegment(i),len);
DEBUG_MSG(DLVL_WARN, "Packet type != 0"); if (!tmpHead.isHeader()){ //not copying the header anymore, should this check isHeader?
} DEBUG_MSG(DLVL_FAIL, "Theora Header read failed!");
oggTracks[tid].lastTime += oggTracks[tid].msPerFrame * (blockSize / oggTracks[tid].channels); return false;
} }
if (oggTracks[tid].codec == THEORA) { DEBUG_MSG(DLVL_WARN, "Theora header, type %d", tmpHead.getHeaderType());
oggTracks[tid].lastTime += oggTracks[tid].msPerFrame; switch (tmpHead.getHeaderType()){
if (it == (tmpPage.getSegmentTableDeque().end() - 1)) { //Case 0 is being handled by parseBeginOfStream
if (oggTracks[tid].idHeader.parseGranuleUpper(oggTracks[tid].lastGran) != oggTracks[tid].idHeader.parseGranuleUpper(tmpPage.getGranulePosition())) { case 1: {
lastPack["keyframe"] = 1ll; myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF);
oggTracks[tid].lastGran = tmpPage.getGranulePosition(); myMeta.tracks[tid].init += (char)(len & 0xFF);
} else { myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
lastPack["interframe"] = 1ll; break;
} }
} case 2: {
} myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF);
lastPack["bpos"] = 0ll; myMeta.tracks[tid].init += (char)(len & 0xFF);
DEBUG_MSG(DLVL_WARN,"Parsed a packet of track %lld, new timestamp %f", tid, oggTracks[tid].lastTime); myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
myMeta.update(lastPack); oggTracks[tid].lastGran = 0;
} oggTracks[tid].parsedHeaders = true;
} else { break;
//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()) { if (myMeta.tracks[tid].codec == "vorbis"){
case 0: { for (int i = 0; i < myPage.getAllSegments().size(); i++){
oggTracks[tid].idHeader = tmpHead; unsigned long len = myPage.getSegmentLen(i);
myMeta.tracks[tid].height = tmpHead.getPICH(); vorbis::header tmpHead((char*)myPage.getSegment(i), len);
myMeta.tracks[tid].width = tmpHead.getPICW(); if (!tmpHead.isHeader()){
myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); DEBUG_MSG(DLVL_FAIL, "Header read failed!");
break; return false;
} }
case 1: { DEBUG_MSG(DLVL_WARN, "Vorbis header, type %d", tmpHead.getHeaderType());
myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); switch (tmpHead.getHeaderType()){
break; //Case 1 is being handled by parseBeginOfStream
} case 3: {
case 2: { //we have the first header stored in contBuffer
myMeta.tracks[tid].codec = "theora"; myMeta.tracks[tid].init += (char)0x02;
myMeta.tracks[tid].trackID = tid; //ID header size
myMeta.tracks[tid].type = "video"; for (int i = 0; i < (oggTracks[tid].contBuffer.size() / 255); i++){
myMeta.tracks[tid].init = std::string(tmpPage.getFullPayload() + offset, (*it)); myMeta.tracks[tid].init += (char)0xFF;
oggTracks[tid].parsedHeaders = true; }
oggTracks[tid].lastGran = 0; myMeta.tracks[tid].init += (char)(oggTracks[tid].contBuffer.size() % 255);
break; //Comment header size
} for (int i = 0; i < (len / 255); i++){
} myMeta.tracks[tid].init += (char)0xFF;
break; }
} myMeta.tracks[tid].init += (char)(len % 255);
case VORBIS: { myMeta.tracks[tid].init += oggTracks[tid].contBuffer;
vorbis::header tmpHead(tmpPage.getFullPayload() + offset, (*it)); oggTracks[tid].contBuffer.clear();
DEBUG_MSG(DLVL_WARN,"Vorbis header, type %d", tmpHead.getHeaderType()); myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
switch (tmpHead.getHeaderType()) { break;
case 1: { }
myMeta.tracks[tid].channels = tmpHead.getAudioChannels(); case 5: {
myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
oggTracks[tid].channels = tmpHead.getAudioChannels(); oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels);
oggTracks[tid].blockSize[0] = 1 << tmpHead.getBlockSize0(); oggTracks[tid].parsedHeaders = true;
oggTracks[tid].blockSize[1] = 1 << tmpHead.getBlockSize1(); break;
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);
} }
} }
lastBytePos = ftell(inFile);
DEBUG_MSG(DLVL_WARN,"End of Loop, @ filepos %lld", lastBytePos);
} }
DEBUG_MSG(DLVL_WARN,"Exited while loop");
for (std::map<long unsigned int, OGG::oggTrack>::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()); std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str());
oFile << myMeta.toJSON().toNetPacked(); oFile << myMeta.toJSON().toNetPacked();
oFile.close(); oFile.close();
//myMeta.toPrettyString(std::cout);
return true; return true;
} }
bool inputOGG::seekNextPage(int tid){ position inputOGG::seekFirstData(long long unsigned int tid){
fseek(inFile, oggTracks[tid].lastPageOffset, SEEK_SET); fseek(inFile, 0, SEEK_SET);
bool res = true; position res;
do { res.time = 0;
res = oggTracks[tid].myPage.read(inFile); res.trackID = tid;
} while(res && snum2tid[oggTracks[tid].myPage.getBitstreamSerialNumber()] != tid); res.segmentNo = 0;
oggTracks[tid].lastPageOffset = ftell(inFile); bool readSuccesfull = true;
oggTracks[tid].nxtSegment = 0; 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; return res;
} }
void inputOGG::getNext(bool smart) { void inputOGG::getNext(bool smart){
if (!sortedSegments.size()){ if (!currentPositions.size()){
for (std::set<int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ lastPack.null();
seekNextPage((*it)); return;
}
} }
if (sortedSegments.size()){ bool lastCompleteSegment = false;
int tid = (*(sortedSegments.begin())).tid; position curPos = *currentPositions.begin();
bool addedPacket = false; currentPositions.erase(currentPositions.begin());
while (!addedPacket){ segment thisSegment;
segPart tmpPart; thisSegment.tid = curPos.trackID;
if (oggTracks[tid].myPage.getSegment(oggTracks[tid].nxtSegment, tmpPart.segData, tmpPart.len)){ thisSegment.time = curPos.time;
if (oggTracks[tid].nxtSegment == 0 && oggTracks[tid].myPage.getHeaderType() && OGG::Continued){ thisSegment.bytepos = curPos.bytepos + curPos.segmentNo;
segment tmpSeg = *(sortedSegments.begin()); unsigned int oldSegNo = curPos.segmentNo;
tmpSeg.parts.push_back(tmpPart); fseek(inFile, curPos.bytepos, SEEK_SET);
sortedSegments.erase(sortedSegments.begin()); OGG::Page curPage;
sortedSegments.insert(tmpSeg); curPage.read(inFile);
}else{ thisSegment.parts.push_back(std::string(curPage.getSegment(curPos.segmentNo), curPage.getSegmentLen(curPos.segmentNo)));
segment tmpSeg; bool readFullPacket = false;
tmpSeg.parts.push_back(tmpPart); if (curPos.segmentNo == curPage.getAllSegments().size() - 1){
tmpSeg.tid = tid; OGG::Page tmpPage;
tmpSeg.time = oggTracks[tid].lastTime; unsigned int bPos;
if (oggTracks[tid].codec == VORBIS) { while (!readFullPacket){
std::string data; bPos = ftell(inFile);//<-- :(
data.append(tmpPart.segData, tmpPart.len); if (!tmpPage.read(inFile)){
unsigned int blockSize = 0; break;
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;
}
} }
} if (tmpPage.getBitstreamSerialNumber() != thisSegment.tid){
std::string data; 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<unsigned int>::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<DTSC::Key>::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) { void inputOGG::trackSelect(std::string trackSpec){
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) {
selectedTracks.clear(); selectedTracks.clear();
size_t index; size_t index;
while (trackSpec != "") { while (trackSpec != ""){
index = trackSpec.find(' '); index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str())); selectedTracks.insert(atoll(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){
if (index != std::string::npos) {
trackSpec.erase(0, index + 1); trackSpec.erase(0, index + 1);
} else { } else {
trackSpec = ""; trackSpec = "";
@ -281,3 +438,9 @@ namespace Mist {
} }

View file

@ -3,27 +3,51 @@
#include <mist/ogg.h> #include <mist/ogg.h>
namespace Mist { namespace Mist {
enum codecType {THEORA, VORBIS};
struct segPart{ struct segPart {
char * segData; char * segData;
unsigned int len; unsigned int len;
}; };
struct segment{ class segment {
bool operator < (const segment & rhs) const { public:
return time < rhs.time || (time == rhs.time && tid < rhs.tid); segment() : time(0), tid(0), bytepos(0), keyframe(0){}
} bool operator < (const segment & rhs) const {
std::vector<segPart> parts; return time < rhs.time || (time == rhs.time && tid < rhs.tid);
unsigned int time; }
unsigned int tid; std::vector<std::string> 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: public:
oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0) { } oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0){ }
codecType codec; codecType codec;
std::string contBuffer;//buffer for continuing pages std::string contBuffer;//buffer for continuing pages
segment myBuffer;
double lastTime; double lastTime;
long long unsigned int lastGran; long long unsigned int lastGran;
bool parsedHeaders; bool parsedHeaders;
@ -37,9 +61,10 @@ namespace Mist {
//vorbis //vorbis
std::deque<vorbis::mode> vModes; std::deque<vorbis::mode> vModes;
char channels; char channels;
long long unsigned int blockSize[2]; unsigned long blockSize[2];
}; unsigned long getBlockSize(unsigned int vModeIndex);
};*/
class inputOGG : public Input { class inputOGG : public Input {
public: public:
inputOGG(Util::Config * cfg); inputOGG(Util::Config * cfg);
@ -47,19 +72,22 @@ namespace Mist {
//Private Functions //Private Functions
bool setup(); bool setup();
bool readHeader(); bool readHeader();
bool seekNextPage(int tid); position seekFirstData(long long unsigned int tid);
void getNext(bool smart = true); void getNext(bool smart = true);
void seek(int seekTime); void seek(int seekTime);
void trackSelect(std::string trackSpec); void trackSelect(std::string trackSpec);
void parseBeginOfStream(OGG::Page & bosPage); void parseBeginOfStream(OGG::Page & bosPage);
std::set<position> currentPositions;
FILE * inFile; FILE * inFile;
std::map<long long int, long long int> snum2tid; std::map<long unsigned int, OGG::oggTrack> oggTracks;//this remembers all metadata for every track
std::map<long long int, oggTrack> oggTracks; std::set<segment> sortedSegments;//probably not needing this
std::set<segment> sortedSegments; 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; typedef Mist::inputOGG mistIn;

View file

@ -163,7 +163,7 @@ namespace Mist {
void Output::negotiatePushTracks() { void Output::negotiatePushTracks() {
int i = 0; int i = 0;
for (std::map<int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){ for (std::map<unsigned int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){
negotiateWithBuffer(it->first); negotiateWithBuffer(it->first);
i++; i++;
} }
@ -286,7 +286,7 @@ namespace Mist {
} }
} }
if (!found){ if (!found){
for (std::map<int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ for (std::map<unsigned int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){
if (trit->second.codec == (*itc).asStringRef()){ if (trit->second.codec == (*itc).asStringRef()){
genCounter++; genCounter++;
found = true; found = true;
@ -326,7 +326,7 @@ namespace Mist {
} }
} }
if (!found){ if (!found){
for (std::map<int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ for (std::map<unsigned int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){
if (trit->second.codec == (*itc).asStringRef()){ if (trit->second.codec == (*itc).asStringRef()){
selectedTracks.insert(trit->first); selectedTracks.insert(trit->first);
found = true; found = true;
@ -438,8 +438,8 @@ namespace Mist {
return; return;
} }
char id[100]; char id[100];
sprintf(id, "%s%lu_%d", streamName.c_str(), trackId, pageNum); snprintf(id, 100, "%s%lu_%d", streamName.c_str(), trackId, pageNum);
curPages[trackId].init(std::string(id),26 * 1024 * 1024); curPages[trackId].init(id, 26 * 1024 * 1024);
if (!(curPages[trackId].mapped)){ if (!(curPages[trackId].mapped)){
DEBUG_MSG(DLVL_FAIL, "Initializing page %s failed", curPages[trackId].name.c_str()); DEBUG_MSG(DLVL_FAIL, "Initializing page %s failed", curPages[trackId].name.c_str());
return; return;
@ -448,7 +448,7 @@ namespace Mist {
} }
/// Prepares all tracks from selectedTracks for seeking to the specified ms position. /// 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; sought = true;
firstTime = Util::getMS() - pos; firstTime = Util::getMS() - pos;
if (!isInitialized){ if (!isInitialized){
@ -457,16 +457,16 @@ namespace Mist {
buffer.clear(); buffer.clear();
currentPacket.null(); currentPacket.null();
updateMeta(); updateMeta();
DEBUG_MSG(DLVL_MEDIUM, "Seeking to %llims", pos); DEBUG_MSG(DLVL_MEDIUM, "Seeking to %llums", pos);
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
seek(*it, pos); 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)); loadPageForKey(tid, getKeyForTime(tid, pos) + (getNextKey?1:0));
if (!curPages.count(tid) || !curPages[tid].mapped){ 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; return false;
} }
sortedPageInfo tmp; sortedPageInfo tmp;
@ -724,7 +724,7 @@ namespace Mist {
} }
if (trackMap.size()){ if (trackMap.size()){
for (std::map<int, int>::iterator it = trackMap.begin(); it != trackMap.end() && tNum < 5; it++){ for (std::map<int, int>::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); char * thisData = playerConn.getData() + (6 * tNum);
thisData[0] = ((tId >> 24) & 0xFF); thisData[0] = ((tId >> 24) & 0xFF);
thisData[1] = ((tId >> 16) & 0xFF); thisData[1] = ((tId >> 16) & 0xFF);
@ -736,7 +736,7 @@ namespace Mist {
} }
}else{ }else{
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end() && tNum < 5; it++){ for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end() && tNum < 5; it++){
int tId = *it; unsigned int tId = *it;
char * thisData = playerConn.getData() + (6 * tNum); char * thisData = playerConn.getData() + (6 * tNum);
thisData[0] = ((tId >> 24) & 0xFF); thisData[0] = ((tId >> 24) & 0xFF);
thisData[1] = ((tId >> 16) & 0xFF); thisData[1] = ((tId >> 16) & 0xFF);

View file

@ -20,7 +20,7 @@ namespace Mist {
} }
return (time == rhs.time && tid < rhs.tid); return (time == rhs.time && tid < rhs.tid);
} }
int tid; unsigned int tid;
long long unsigned int time; long long unsigned int time;
unsigned int offset; unsigned int offset;
}; };
@ -53,8 +53,8 @@ namespace Mist {
//non-virtual generic functions //non-virtual generic functions
int run(); int run();
void stats(); void stats();
void seek(long long pos); void seek(unsigned long long pos);
bool seek(int tid, long long pos, bool getNextKey = false); bool seek(unsigned int tid, unsigned long long pos, bool getNextKey = false);
void stop(); void stop();
void setBlocking(bool blocking); void setBlocking(bool blocking);
long unsigned int getMainSelectedTrack(); long unsigned int getMainSelectedTrack();

View file

@ -12,7 +12,7 @@ namespace Mist {
audioTrack = 0; audioTrack = 0;
JSON::Value & vidCapa = capa["codecs"][0u][0u]; JSON::Value & vidCapa = capa["codecs"][0u][0u];
JSON::Value & audCapa = capa["codecs"][0u][1u]; JSON::Value & audCapa = capa["codecs"][0u][1u];
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
for (JSON::ArrIter itb = vidCapa.ArrBegin(); itb != vidCapa.ArrEnd(); itb++){ for (JSON::ArrIter itb = vidCapa.ArrBegin(); itb != vidCapa.ArrEnd(); itb++){
if (it->second.codec == (*itb).asStringRef()){ if (it->second.codec == (*itb).asStringRef()){
videoTracks.insert(it->first); videoTracks.insert(it->first);

View file

@ -10,14 +10,14 @@ namespace Mist {
result << "#EXTM3U\r\n"; result << "#EXTM3U\r\n";
int audioId = -1; int audioId = -1;
std::string audioName; std::string audioName;
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.codec == "AAC"){ if (it->second.codec == "AAC"){
audioId = it->first; audioId = it->first;
audioName = it->second.getIdentifier(); audioName = it->second.getIdentifier();
break; break;
} }
} }
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.codec == "H264"){ if (it->second.codec == "H264"){
int bWidth = it->second.bps * 2; int bWidth = it->second.bps * 2;
if (audioId != -1){ if (audioId != -1){
@ -266,7 +266,7 @@ namespace Mist {
return 1; return 1;
} }
//loop trough all the tracks //loop trough all the tracks
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
//return "too late" if one track is past this point //return "too late" if one track is past this point
if (ms < it->second.firstms){ if (ms < it->second.firstms){
return -1; return -1;

View file

@ -295,13 +295,13 @@ namespace Mist {
"MajorVersion=\"2\" " "MajorVersion=\"2\" "
"MinorVersion=\"0\" " "MinorVersion=\"0\" "
"TimeScale=\"10000000\" "; "TimeScale=\"10000000\" ";
std::deque<std::map<int, DTSC::Track>::iterator> audioIters; std::deque<std::map<unsigned int, DTSC::Track>::iterator> audioIters;
std::deque<std::map<int, DTSC::Track>::iterator> videoIters; std::deque<std::map<unsigned int, DTSC::Track>::iterator> videoIters;
long long int maxWidth = 0; long long int maxWidth = 0;
long long int maxHeight = 0; long long int maxHeight = 0;
long long int minWidth = 99999999; long long int minWidth = 99999999;
long long int minHeight = 99999999; long long int minHeight = 99999999;
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
if (it->second.codec == "AAC") { if (it->second.codec == "AAC") {
audioIters.push_back(it); audioIters.push_back(it);
} }
@ -343,7 +343,7 @@ namespace Mist {
"Chunks=\"" << (*audioIters.begin())->second.keys.size() << "\" " "Chunks=\"" << (*audioIters.begin())->second.keys.size() << "\" "
"Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n"; "Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n";
int index = 0; int index = 0;
for (std::deque<std::map<int, DTSC::Track>::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) { for (std::deque<std::map<unsigned int, DTSC::Track>::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) {
Result << "<QualityLevel " Result << "<QualityLevel "
"Index=\"" << index << "\" " "Index=\"" << index << "\" "
"Bitrate=\"" << (*it)->second.bps * 8 << "\" " "Bitrate=\"" << (*it)->second.bps * 8 << "\" "
@ -388,7 +388,7 @@ namespace Mist {
"DisplayWidth=\"" << maxWidth << "\" " "DisplayWidth=\"" << maxWidth << "\" "
"DisplayHeight=\"" << maxHeight << "\">\n"; "DisplayHeight=\"" << maxHeight << "\">\n";
int index = 0; int index = 0;
for (std::deque<std::map<int, DTSC::Track>::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) { for (std::deque<std::map<unsigned int, DTSC::Track>::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) {
//Add video qualities //Add video qualities
Result << "<QualityLevel " Result << "<QualityLevel "
"Index=\"" << index << "\" " "Index=\"" << index << "\" "
@ -450,7 +450,7 @@ namespace Mist {
void OutHSS::initialize() { void OutHSS::initialize() {
Output::initialize(); Output::initialize();
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++) { for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++) {
keyTimes[it->first].insert(it2->getTime()); keyTimes[it->first].insert(it2->getTime());
} }

View file

@ -51,7 +51,7 @@ namespace Mist {
if (H.GetVar("callback") != ""){jsonp = H.GetVar("callback");} if (H.GetVar("callback") != ""){jsonp = H.GetVar("callback");}
if (H.GetVar("jsonp") != ""){jsonp = H.GetVar("jsonp");} if (H.GetVar("jsonp") != ""){jsonp = H.GetVar("jsonp");}
initialize(); initialize();
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.type == "meta" ){ if (it->second.type == "meta" ){
selectedTracks.insert(it->first); selectedTracks.insert(it->first);
} }

View file

@ -0,0 +1,208 @@
#include "output_progressive_ogg.h"
#include <mist/bitstream.h>
#include <mist/defines.h>
#include <algorithm>
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<long long unsigned int, OGG::Page>::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<std::string> & 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<int, std::deque<std::string> > initData;
OGG::oggSegment newSegment;
for (std::set<long unsigned int>::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<long unsigned int>::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();
}
}
}

View file

@ -0,0 +1,28 @@
#include "output_http.h"
#include <mist/ogg.h>
#include <mist/http_parser.h>
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<std::string> & output);
protected:
HTTP::Parser HTTP_R;//Received HTTP
HTTP::Parser HTTP_S;//Sent HTTP
std::map <long long unsigned int, OGG::Page > pageBuffer; //OGG specific variables
};
}
typedef Mist::OutProgressiveOGG mistOut;