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
$(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\""

View file

@ -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: {

View file

@ -1,5 +1,5 @@
#include <cstdlib>
#include <cstdio>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
@ -8,41 +8,83 @@
#include <mist/ogg.h>
#include <mist/config.h>
#include <mist/theora.h>
///\todo rewrite this analyser.
namespace Analysers {
std::string Opus_prettyPacket(char * part,int len){
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)";
@ -80,28 +122,24 @@ namespace Analysers{
std::map<int, std::string> 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(oggBuffer)){//reading ogg to string
while (oggPage.read(stdin)){ //reading ogg to string
//print the Ogg page details, if requested
if (conf.getBool("pages")){
std::cout << oggPage.toPrettyString() << std::endl;
printf("%s", oggPage.toPrettyString().c_str());
}
//attempt to detect codec if this is the first page of a stream
if (oggPage.getHeaderType() & OGG::BeginOfStream){
if (memcmp("theora",oggPage.getFullPayload() + 1,6) == 0){
if (memcmp("theora", oggPage.getSegment(0) + 1, 6) == 0){
sn2Codec[oggPage.getBitstreamSerialNumber()] = "Theora";
}
if (memcmp("vorbis",oggPage.getFullPayload() + 1,6) == 0){
if (memcmp("vorbis", oggPage.getSegment(0) + 1, 6) == 0){
sn2Codec[oggPage.getBitstreamSerialNumber()] = "Vorbis";
}
if (memcmp("OpusHead",oggPage.getFullPayload(),8) == 0){
if (memcmp("OpusHead", oggPage.getSegment(0), 8) == 0){
sn2Codec[oggPage.getBitstreamSerialNumber()] = "Opus";
}
if (sn2Codec[oggPage.getBitstreamSerialNumber()] != ""){
@ -114,36 +152,43 @@ namespace Analysers{
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);
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++;
}
offset += len;
if (oggPage.getHeaderType() != OGG::Continued || i){
numParts++;
}
}
std::cout << tmpHeader.toPrettyString(4);
}
} 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);
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);
}
offset += len;
}
} 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;
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;
@ -159,14 +204,14 @@ namespace Analysers{
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;
const char * str_data = part + 12 + vendor_len;
unsigned int strings = str_data[0] + (str_data[1] << 8) + (str_data[2] << 16) + (str_data[3] << 24);
std::cout << " Tags: (" << strings << ")" << std::endl;
str_data += 4;
for (unsigned int j = 0; j < strings; j++){
unsigned int strlen = str_data[0] + (str_data[1] << 8) + (str_data[2] << 16) + (str_data[3] << 24);
str_data += 4;
std::cout << " [" << j << "] " << std::string(str_data, strlen) << std::endl;
std::cout << " [" << j << "] " << std::string((char *) str_data, strlen) << std::endl;
str_data += strlen;
}
}
@ -176,7 +221,7 @@ namespace Analysers{
offset += len;
}
}
}
std::cout << std::endl;
}
return 0;
}
@ -186,3 +231,4 @@ int main(int argc, char ** argv){
return Analysers::analyseOGG(argc, argv);
}

View file

@ -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;
}

View file

@ -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<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);
}
}
@ -204,8 +204,8 @@ namespace Mist {
DEBUG_MSG(DLVL_DONTEVEN,"Parsing the header");
selectedTracks.clear();
std::stringstream trackSpec;
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
for (std::map<unsigned int, DTSC::Track>::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<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()){
hasKeySizes = false;
break;
}
}
INFO_MSG("%s", (hasKeySizes ? "hasKeysizes" : "noHasKeysizes"));
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];
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++){
@ -252,7 +254,7 @@ namespace Mist {
getNext();
while(lastPack){//loop through all
int tid = lastPack.getTrackId();
unsigned int tid = lastPack.getTrackId();
if (!tid){
getNext(false);
continue;
@ -269,11 +271,11 @@ namespace Mist {
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.
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() == curData[tid].partNum){
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;
@ -288,25 +290,29 @@ namespace Mist {
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);
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<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 (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++){
for (std::map<unsigned int, DTSC::Track>::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<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 (!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;
}

View file

@ -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<int> selectedTracks;
std::set<unsigned int> selectedTracks;
bool isBuffer;

View file

@ -43,7 +43,7 @@ namespace Mist {
inputBuffer::~inputBuffer(){
if (myMeta.tracks.size()){
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)){}
}
}
@ -52,7 +52,7 @@ namespace Mist {
void inputBuffer::updateMeta(){
long long unsigned int firstms = 0xFFFFFFFFFFFFFFFFull;
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){
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<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.firstms < firstVideo || firstVideo == 1){
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
if (it->second.type != "video"){
if (it->second.keys.size() < 2 || it->second.keys[1].getTime() > firstVideo){

View file

@ -9,33 +9,52 @@
#include <mist/ogg.h>
#include <mist/defines.h>
#include <mist/bitstream.h>
#include "input_ogg.h"
///\todo Whar be Opus support?
namespace Mist {
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;
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");
@ -45,223 +64,362 @@ namespace Mist {
return true;
}
///\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;
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();
//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
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<unsigned int>::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));
bool readAllHeaders = true;
for (std::map<long unsigned int, OGG::oggTrack>::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].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
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].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;
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;
}
}
break;
}
case VORBIS: {
vorbis::header tmpHead(tmpPage.getFullPayload() + offset, (*it));
}
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: {
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 1 is being handled by parseBeginOfStream
case 3: {
myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
//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].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");
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels);
DEBUG_MSG(DLVL_WARN,"Set vmodevalues");
oggTracks[tid].parsedHeaders = true;
break;
}
}
break;
}
}
offset += (*it);
}
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);
}
}
lastBytePos = ftell(inFile);
DEBUG_MSG(DLVL_WARN,"End of Loop, @ filepos %lld", lastBytePos);
getNext();
while (lastPack){
myMeta.update(lastPack);
getNext();
}
DEBUG_MSG(DLVL_WARN,"Exited while loop");
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<int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
seekNextPage((*it));
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)){
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;
}
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
}
}
std::string data;
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){
DEBUG_MSG(DLVL_WARN,"Seeking is not yet supported for ogg files");
//Do nothing, seeking is not yet implemented for ogg
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::trackSelect(std::string trackSpec){
@ -269,8 +427,7 @@ namespace Mist {
size_t index;
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);
selectedTracks.insert(atoll(trackSpec.substr(0, index).c_str()));
if (index != std::string::npos){
trackSpec.erase(0, index + 1);
} else {
@ -281,3 +438,9 @@ namespace Mist {
}

View file

@ -3,27 +3,51 @@
#include <mist/ogg.h>
namespace Mist {
enum codecType {THEORA, VORBIS};
struct segPart {
char * segData;
unsigned int len;
};
struct segment{
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<segPart> parts;
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);
};
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){ }
codecType codec;
std::string contBuffer;//buffer for continuing pages
segment myBuffer;
double lastTime;
long long unsigned int lastGran;
bool parsedHeaders;
@ -37,8 +61,9 @@ namespace Mist {
//vorbis
std::deque<vorbis::mode> vModes;
char channels;
long long unsigned int blockSize[2];
};
unsigned long blockSize[2];
unsigned long getBlockSize(unsigned int vModeIndex);
};*/
class inputOGG : public Input {
public:
@ -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<position> currentPositions;
FILE * inFile;
std::map<long long int, long long int> snum2tid;
std::map<long long int, oggTrack> oggTracks;
std::set<segment> sortedSegments;
std::map<long unsigned int, OGG::oggTrack> oggTracks;//this remembers all metadata for every track
std::set<segment> 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;

View file

@ -163,7 +163,7 @@ namespace Mist {
void Output::negotiatePushTracks() {
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);
i++;
}
@ -286,7 +286,7 @@ namespace Mist {
}
}
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()){
genCounter++;
found = true;
@ -326,7 +326,7 @@ namespace Mist {
}
}
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()){
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<long unsigned int>::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<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);
thisData[0] = ((tId >> 24) & 0xFF);
thisData[1] = ((tId >> 16) & 0xFF);
@ -736,7 +736,7 @@ namespace Mist {
}
}else{
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);
thisData[0] = ((tId >> 24) & 0xFF);
thisData[1] = ((tId >> 16) & 0xFF);

View file

@ -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();

View file

@ -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<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++){
if (it->second.codec == (*itb).asStringRef()){
videoTracks.insert(it->first);

View file

@ -10,14 +10,14 @@ namespace Mist {
result << "#EXTM3U\r\n";
int audioId = -1;
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"){
audioId = it->first;
audioName = it->second.getIdentifier();
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"){
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<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
if (ms < it->second.firstms){
return -1;

View file

@ -295,13 +295,13 @@ namespace Mist {
"MajorVersion=\"2\" "
"MinorVersion=\"0\" "
"TimeScale=\"10000000\" ";
std::deque<std::map<int, DTSC::Track>::iterator> audioIters;
std::deque<std::map<int, DTSC::Track>::iterator> videoIters;
std::deque<std::map<unsigned int, DTSC::Track>::iterator> audioIters;
std::deque<std::map<unsigned int, DTSC::Track>::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<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") {
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<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 "
"Index=\"" << index << "\" "
"Bitrate=\"" << (*it)->second.bps * 8 << "\" "
@ -388,7 +388,7 @@ namespace Mist {
"DisplayWidth=\"" << maxWidth << "\" "
"DisplayHeight=\"" << maxHeight << "\">\n";
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
Result << "<QualityLevel "
"Index=\"" << index << "\" "
@ -450,7 +450,7 @@ namespace Mist {
void OutHSS::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++) {
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("jsonp") != ""){jsonp = H.GetVar("jsonp");}
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" ){
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;