Ogg optimizes

This commit is contained in:
Oswald Auguste de Bruin 2013-10-07 16:26:51 +02:00 committed by Thulinma
parent 63913f361a
commit d8b7a3ea1c
5 changed files with 314 additions and 66 deletions

View file

@ -20,6 +20,7 @@
#include <mist/config.h>
#include <mist/stream.h>
#include <mist/timing.h>
#include "../converters/oggconv.h"
///\brief Holds everything unique to HTTP Connectors.
namespace Connector_HTTP {
@ -36,8 +37,9 @@ namespace Connector_HTTP {
std::string streamname;//Will contain the name of the stream.
//OGG specific variables
OGG::headerPages oggMeta;
OGG::Page curOggPage;
//OGG::headerPages oggMeta;
//OGG::Page curOggPage;
OGG::converter oggConv;
std::map <long long unsigned int, std::vector<JSON::Value> > DTSCBuffer;
std::set <long long unsigned int> sendReady;
//std::map <long long unsigned int, long long unsigned int> prevGran;
@ -141,7 +143,7 @@ namespace Connector_HTTP {
if (audioID != -1){
cmd << " " << audioID;
}
cmd << "\ns " << seek_sec << "\np\n";
cmd << "\np\n";
ss.SendNow(cmd.str().c_str(), cmd.str().size());
inited = true;
}
@ -159,8 +161,8 @@ namespace Connector_HTTP {
HTTP_S.protocol = "HTTP/1.0";
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
//Fill in ogg header here
oggMeta.readDTSCHeader(Strm.metadata);
conn.SendNow((char*)oggMeta.parsedPages.c_str(), oggMeta.parsedPages.size());
oggConv.readDTSCHeader(Strm.metadata);
conn.SendNow((char*)oggConv.parsedPages.c_str(), oggConv.parsedPages.size());
progressive_has_sent_header = true;
//setting sendReady to not ready
sendReady.clear();
@ -168,20 +170,11 @@ namespace Connector_HTTP {
//parse DTSC to Ogg here
if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){
currID = Strm.getPacket()["trackid"].asInt();
currGran = Strm.getPacket()["granule"].asInt();
if (DTSCBuffer.count(currID) && !DTSCBuffer[currID].empty()){
prevGran = DTSCBuffer[currID][0]["granule"].asInt();
}else{
prevGran = 0;
}
if ((prevGran != 0 && (prevGran == -1 || currGran != prevGran)) ){
curOggPage.readDTSCVector(DTSCBuffer[currID], oggMeta.DTSCID2OGGSerial[currID], oggMeta.DTSCID2seqNum[currID]);
conn.SendNow((char*)curOggPage.getPage(), curOggPage.getPageSize());
DTSCBuffer[currID].clear();
sendReady.insert(currID);
oggMeta.DTSCID2seqNum[currID]++;
}
DTSCBuffer[currID].push_back(Strm.getPacket());
std::string tmpString = oggConv.readDTSCVector(DTSCBuffer[currID]);
conn.SendNow((char*)tmpString.c_str(), tmpString.size());
DTSCBuffer[currID].clear();
sendReady.insert(currID);
}
if (Strm.lastType() == DTSC::PAUSEMARK){
conn.close();

View file

@ -2,6 +2,7 @@
#include<vector>
#include <queue>
#include <stdlib.h>
#include "oggconv.h"
#include <mist/timing.h>
#include <mist/dtsc.h>
@ -14,21 +15,14 @@
namespace Converters{
int DTSC2OGG(Util::Config & conf){
DTSC::File DTSCFile(conf.getString("filename"));
//JSON::Value meta = DTSCFile.getMeta();
srand (Util::getMS());//randomising with milliseconds from boot
std::vector<unsigned int> curSegTable;
OGG::headerPages oggMeta;
char* curNewPayload;
OGG::converter oggMeta;
//Creating ID headers for theora and vorbis
DTSC::readOnlyMeta fileMeta = DTSCFile.getMeta();
DTSC::Meta giveMeta;
for ( std::map<int,DTSC::readOnlyTrack>::iterator it = fileMeta.tracks.begin(); it != fileMeta.tracks.end(); it ++) {
std::cerr << "TrackID: " << it->first << std::endl;
giveMeta.tracks[it->first].trackID = fileMeta.tracks[it->first].trackID;
giveMeta.tracks[it->first].idHeader = fileMeta.tracks[it->first].idHeader;
giveMeta.tracks[it->first].init = fileMeta.tracks[it->first].init;
giveMeta.tracks[it->first].commentHeader = fileMeta.tracks[it->first].commentHeader;
}
DTSC::Meta giveMeta(fileMeta);
oggMeta.readDTSCHeader(giveMeta);
std::cout << oggMeta.parsedPages;//outputting header pages
@ -49,28 +43,20 @@ namespace Converters{
}else{
prevGran = 0;
}
if (prevGran != 0 && (currGran != prevGran)){
curOggPage.readDTSCVector(DTSCBuffer[currID], oggMeta.DTSCID2OGGSerial[currID], oggMeta.DTSCID2seqNum[currID]);
std::cout << std::string((char*)curOggPage.getPage(), curOggPage.getPageSize());
if (!DTSCBuffer[currID].empty()){
std::cout << oggMeta.readDTSCVector(DTSCBuffer[currID]);
DTSCBuffer[currID].clear();
oggMeta.DTSCID2seqNum[currID]++;
}
DTSCBuffer[currID].push_back(DTSCFile.getJSON());
DTSCFile.parseNext();
}
//outputting end of stream pages
for (
std::map< long long int, std::vector<JSON::Value> >::iterator it = DTSCBuffer.begin();
it != DTSCBuffer.end();
it++
){
for (std::map< long long int, std::vector<JSON::Value> >::iterator it = DTSCBuffer.begin(); it != DTSCBuffer.end(); it++){
if (!DTSCBuffer[it->first].empty() && DTSCBuffer[it->first][0]["data"].asString() != ""){
curOggPage.readDTSCVector(DTSCBuffer[it->first], oggMeta.DTSCID2OGGSerial[it->first], oggMeta.DTSCID2seqNum[it->first]);
std::cout << std::string((char*)curOggPage.getPage(), curOggPage.getPageSize());
std::cout << oggMeta.readDTSCVector(DTSCBuffer[it->first]);
}
}
return 0;
}
}

View file

@ -10,6 +10,7 @@
#include <mist/vorbis.h>
#include <mist/config.h>
#include <mist/json.h>
#include <mist/bitstream.h>
namespace Converters{
enum codecType {THEORA, VORBIS};
@ -19,11 +20,18 @@ namespace Converters{
oggTrack() : lastTime(0), parsedHeaders(false) { }
codecType codec;
std::string name;
std::string contBuffer;//buffer for continuing pages
long long unsigned int dtscID;
double lastTime;
long long unsigned int lastGran;
bool parsedHeaders;
//Codec specific elements
//theora
theora::header idHeader;//needed to determine keyframe
//vorbis
std::deque<vorbis::mode> vModes;
char channels;
long long unsigned int blockSize[2];
};
int OGG2DTSC(){
@ -64,7 +72,7 @@ namespace Converters{
trackData[sNum].codec = VORBIS;
vorbis::header tempHead;
tempHead.read(oggPage.getFullPayload(), oggPage.getPayloadSize());
mspfv = ntohl(tempHead.getAudioSampleRate()) / 1000;
mspfv = (double)1000 / ntohl(tempHead.getAudioSampleRate());
}else{
std::cerr << "Unknown Codec, " << std::string(oggPage.getFullPayload()+1, 6)<<" skipping" << std::endl;
continue;
@ -79,34 +87,61 @@ namespace Converters{
int offset = 0;
for (std::deque<unsigned int>::iterator it = oggPage.getSegmentTableDeque().begin(); it != oggPage.getSegmentTableDeque().end(); it++){
if (trackData[sNum].parsedHeaders){
//output DTSC packet
DTSCOut.null();//clearing DTSC buffer
DTSCOut["trackid"] = (long long)trackData[sNum].dtscID;
long long unsigned int temp = oggPage.getGranulePosition();
DTSCOut["granule"] = (long long)temp;
DTSCOut["time"] = (long long)trackData[sNum].lastTime;
if (trackData[sNum].codec == THEORA){
trackData[sNum].lastTime += (mspft / 4);
//if we are dealing with the last segment which is a part of a later continued segment
if (it == (oggPage.getSegmentTableDeque().end()-1) && oggPage.getPageSegments() == 255 && oggPage.getSegmentTable()[254] == 255 ){
//put in buffer
trackData[sNum].contBuffer += std::string(oggPage.getFullPayload()+offset, (*it));
}else{
trackData[sNum].lastTime += (mspfv / 16);
}
DTSCOut["data"] = std::string(oggPage.getFullPayload()+offset, (*it)); //segment content put in JSON
if (trackData[sNum].codec == THEORA){
if (trackData[sNum].idHeader.parseGranuleLower(temp) == 0){ //granule mask equals zero when on keyframe
DTSCOut["keyframe"] = 1;
//output DTSC packet
DTSCOut.null();//clearing DTSC buffer
DTSCOut["trackid"] = (long long)trackData[sNum].dtscID;
long long unsigned int temp = oggPage.getGranulePosition();
DTSCOut["time"] = (long long)trackData[sNum].lastTime;
if (trackData[sNum].contBuffer != ""){
//if a big segment is ending on this page, output buffer
DTSCOut["data"] = trackData[sNum].contBuffer + std::string(oggPage.getFullPayload()+offset, (*it));
DTSCOut["comment"] = "Using buffer";
trackData[sNum].contBuffer = "";
}else{
DTSCOut["interframe"] = 1;
DTSCOut["data"] = std::string(oggPage.getFullPayload()+offset, (*it)); //segment content put in JSON
}
DTSCOut["time"] = (long long)trackData[sNum].lastTime;
if (trackData[sNum].codec == THEORA){
trackData[sNum].lastTime += mspft;
}else{
//Getting current blockSize
unsigned int blockSize = 0;
Utils::bitstreamLSBF packet;
packet.append(DTSCOut["data"].asString());
if (packet.get(1) == 0){
blockSize = trackData[sNum].blockSize[trackData[sNum].vModes[packet.get(vorbis::ilog(trackData[sNum].vModes.size()-1))].blockFlag];
}else{
std::cerr << "Warning! packet type != 0" << std::endl;
}
trackData[sNum].lastTime += mspfv * (blockSize/trackData[sNum].channels);
}
if (trackData[sNum].codec == THEORA){//marking keyframes
if (it == (oggPage.getSegmentTableDeque().end() - 1)){
//if we are in the vicinity of a new keyframe
if (trackData[sNum].idHeader.parseGranuleUpper(trackData[sNum].lastGran) != trackData[sNum].idHeader.parseGranuleUpper(temp)){
//try to mark right
long long unsigned int temper = trackData[sNum].idHeader.parseGranuleUpper(temp) - trackData[sNum].idHeader.parseGranuleUpper(trackData[sNum].lastGran);
DTSCOut["keyframe"] = 1;
trackData[sNum].lastGran = temp;
}else{
DTSCOut["interframe"] = 1;
}
}
}
// Ending packet
if (oggPage.typeContinue()){//Continuing page
DTSCOut["OggCont"] = 1;
}
if (oggPage.typeEOS()){//ending page of ogg stream
DTSCOut["OggEOS"] = 1;
}
std::cout << DTSCOut.toNetPacked();
}
// Ending packet
if (oggPage.typeContinue()){//Continuing page
DTSCOut["OggCont"] = 1;
}
if (oggPage.typeEOS()){//ending page
DTSCOut["OggEOS"] = 1;
}
std::cout << DTSCOut.toNetPacked();
}else{//if we ouput a header:
//switch on codec
switch(trackData[sNum].codec){
@ -132,6 +167,7 @@ namespace Converters{
DTSCHeader["tracks"][trackData[sNum].name]["init"] = std::string(oggPage.getFullPayload()+offset, (*it));
headerSeen --;
trackData[sNum].parsedHeaders = true;
trackData[sNum].lastGran = 0;
break;
}
}
@ -145,6 +181,9 @@ namespace Converters{
case 1:{
DTSCHeader["tracks"][trackData[sNum].name]["channels"] = (long long)vHead.getAudioChannels();
DTSCHeader["tracks"][trackData[sNum].name]["idheader"] = std::string(oggPage.getFullPayload()+offset, (*it));
trackData[sNum].channels = vHead.getAudioChannels();
trackData[sNum].blockSize[0] = 1 << vHead.getBlockSize0();
trackData[sNum].blockSize[1] = 1 << vHead.getBlockSize1();
break;
}
case 3:{
@ -156,6 +195,8 @@ namespace Converters{
DTSCHeader["tracks"][trackData[sNum].name]["trackid"] = (long long)trackData[sNum].dtscID;
DTSCHeader["tracks"][trackData[sNum].name]["type"] = "audio";
DTSCHeader["tracks"][trackData[sNum].name]["init"] = std::string(oggPage.getFullPayload()+offset, (*it));
//saving modes into deque
trackData[sNum].vModes = vHead.readModeDeque(trackData[sNum].channels);
headerSeen --;
trackData[sNum].parsedHeaders = true;
break;

199
src/converters/oggconv.cpp Normal file
View file

@ -0,0 +1,199 @@
#include"oggconv.h"
#include <stdlib.h>
#include <mist/bitstream.h>
namespace OGG{
void converter::readDTSCHeader(DTSC::Meta & meta){
//pages.clear();
parsedPages = "";
Page curOggPage;
srand (Util::getMS());//randomising with milliseconds from boot
std::vector<unsigned int> curSegTable;
//trackInf.clear();
//Creating ID headers for theora and vorbis
for ( std::map<int,DTSC::Track>::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it ++) {
curOggPage.clear();
curOggPage.setVersion();
curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream
curOggPage.setGranulePosition(0);
trackInf[it->second.trackID].OGGSerial = rand() % 0xFFFFFFFE +1; //initialising on a random not 0 number
curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial);
trackInf[it->second.trackID].seqNum = 0;
curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++);
curSegTable.clear();
curSegTable.push_back(it->second.idHeader.size());
curOggPage.setSegmentTable(curSegTable);
curOggPage.setPayload((char*)it->second.idHeader.c_str(), it->second.idHeader.size());
curOggPage.setCRCChecksum(curOggPage.calcChecksum());
//pages.push_back(curOggPage);
parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize());
trackInf[it->second.trackID].codec = it->second.codec;
if (it->second.codec == "theora"){
trackInf[it->second.trackID].lastKeyFrame = 1;
trackInf[it->second.trackID].sinceKeyFrame = 0;
theora::header tempHead;
std::string tempString = it->second.idHeader;
tempHead.read((char*)tempString.c_str(),42);
trackInf[it->second.trackID].significantValue = tempHead.getKFGShift();
}else if (it->second.codec == "vorbis"){
trackInf[it->second.trackID].lastKeyFrame = 0;
trackInf[it->second.trackID].sinceKeyFrame = 0;
trackInf[it->second.trackID].prevBlockFlag = -1;
vorbis::header tempHead;
std::string tempString = it->second.idHeader;
tempHead.read((char*)tempString.c_str(),tempString.size());
trackInf[it->second.trackID].significantValue = tempHead.getAudioSampleRate() / tempHead.getAudioChannels();
if (tempHead.getBlockSize0() <= tempHead.getBlockSize1()){
trackInf[it->second.trackID].blockSize[0] = tempHead.getBlockSize0();
trackInf[it->second.trackID].blockSize[1] = tempHead.getBlockSize1();
}else{
trackInf[it->second.trackID].blockSize[0] = tempHead.getBlockSize1();
trackInf[it->second.trackID].blockSize[1] = tempHead.getBlockSize0();
}
char audioChannels = tempHead.getAudioChannels();
//getting modes
tempString = it->second.init;
tempHead.read((char*)tempString.c_str(),tempString.size());
trackInf[it->second.trackID].vorbisModes = tempHead.readModeDeque(audioChannels);
trackInf[it->second.trackID].hadFirst = false;
}
}
//Creating remaining headers for theora and vorbis
//for tracks in header
//create standard page with comment (empty) en setup header(init)
for ( std::map<int,DTSC::Track>::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it ++) {
curOggPage.clear();
curOggPage.setVersion();
curOggPage.setHeaderType(0);//headertype 0 = normal
curOggPage.setGranulePosition(0);
curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial);
curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++);
curSegTable.clear();
curSegTable.push_back(it->second.commentHeader.size());
curSegTable.push_back(it->second.init.size());
curOggPage.setSegmentTable(curSegTable);
std::string fullHeader = it->second.commentHeader + it->second.init;
curOggPage.setPayload((char*)fullHeader.c_str(),fullHeader.size());
curOggPage.setCRCChecksum(curOggPage.calcChecksum());
parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize());
}
}
std::string converter::readDTSCVector(std::vector <JSON::Value> DTSCVec){
Page retVal;
int typeFlag = 0;//flag to remember if the page has a continued segment
std::string pageBuffer = "";
long long int DTSCID = DTSCVec[0]["trackid"].asInt();
std::vector<unsigned int> curSegTable;
std::string dataBuffer;
long long unsigned int lastGran = 0;
for (unsigned int i = 0; i < DTSCVec.size(); i++){
OGG::Page tempPage;
tempPage.setSegmentTable(curSegTable);
if (DTSCVec[i]["data"].asString().size() >= (255-tempPage.getPageSegments())*255){//if segment is too big
//Put page in Buffer and start next page
if (!curSegTable.empty()){
//output page
retVal.clear();
retVal.setVersion();
retVal.setHeaderType(typeFlag);//headertype 0 = normal
retVal.setGranulePosition(lastGran);
retVal.setBitstreamSerialNumber(trackInf[DTSCID].OGGSerial);
retVal.setPageSequenceNumber(trackInf[DTSCID].seqNum);
retVal.setSegmentTable(curSegTable);
retVal.setPayload((char*)dataBuffer.c_str(), dataBuffer.size());
retVal.setCRCChecksum(retVal.calcChecksum());
trackInf[DTSCID].seqNum++;
pageBuffer += std::string((char*)retVal.getPage(), retVal.getPageSize());
curSegTable.clear();
dataBuffer = "";
}
std::string remainingData = DTSCVec[i]["data"].asString();
bool firstLoop;
typeFlag = 0;
while (remainingData.size() > 255*255){
//output part of the segment
//granule -1
curSegTable.clear();
curSegTable.push_back(255*255);///\TODO optimise this
retVal.clear();
retVal.setVersion();
retVal.setHeaderType(typeFlag);//normal Page
retVal.setGranulePosition(-1);
retVal.setBitstreamSerialNumber(trackInf[DTSCID].OGGSerial);
retVal.setPageSequenceNumber(trackInf[DTSCID].seqNum);
retVal.setSegmentTable(curSegTable);
retVal.setPayload((char*)remainingData.substr(0,255*255).c_str(), 255*255);
retVal.setCRCChecksum(retVal.calcChecksum());
trackInf[DTSCID].seqNum++;
pageBuffer += std::string((char*)retVal.getPage(), retVal.getPageSize());
remainingData = remainingData.substr(255*255);
typeFlag = 1;//1 = continued page
}
//output last remaining data
curSegTable.clear();
curSegTable.push_back(remainingData.size());
dataBuffer += remainingData;
}else{//build data for page
curSegTable.push_back(DTSCVec[i]["data"].asString().size());
dataBuffer += DTSCVec[i]["data"].asString();
}
//lastGran = calcGranule(DTSCID, DTSCVec[i]["keyframe"].asBool());
//calculating granule position
if (trackInf[DTSCID].codec == "theora"){
if (DTSCVec[i]["keyframe"].asBool()){
trackInf[DTSCID].lastKeyFrame += trackInf[DTSCID].sinceKeyFrame + 1;
trackInf[DTSCID].sinceKeyFrame = 0;
}else{
trackInf[DTSCID].sinceKeyFrame ++;
}
lastGran = (trackInf[DTSCID].lastKeyFrame << trackInf[DTSCID].significantValue) + trackInf[DTSCID].sinceKeyFrame;
} else if (trackInf[DTSCID].codec == "vorbis"){
//decode DTSCVec[i]["data"].asString() for mode index
Utils::bitstreamLSBF packet;
packet.append(DTSCVec[i]["data"].asString());
//calculate amount of samples associated with that block (from ID header)
//check mode block in deque for index
int curPCMSamples = 0;
if (packet.get(1) == 0){
int tempModes = vorbis::ilog(trackInf[DTSCID].vorbisModes.size()-1);
int tempPacket = packet.get(tempModes);
int curBlockFlag = trackInf[DTSCID].vorbisModes[tempPacket].blockFlag;
curPCMSamples = (1 << trackInf[DTSCID].blockSize[curBlockFlag]);
if (trackInf[DTSCID].prevBlockFlag!= -1){
if (curBlockFlag == trackInf[DTSCID].prevBlockFlag){
curPCMSamples /= 2;
}else{
curPCMSamples -= (1 << trackInf[DTSCID].blockSize[0]) / 4 + (1 << trackInf[DTSCID].blockSize[1]) / 4;
}
}
trackInf[DTSCID].sinceKeyFrame = (1 << trackInf[DTSCID].blockSize[curBlockFlag]);
trackInf[DTSCID].prevBlockFlag = curBlockFlag;
}else{
std::cerr << "Error, Vorbis packet type !=0" << std::endl;
}
//add to granule position
trackInf[DTSCID].lastKeyFrame += curPCMSamples;
lastGran = trackInf[DTSCID].lastKeyFrame;
}
}
//last parts of page put out
if (!curSegTable.empty()){
retVal.clear();
retVal.setVersion();
retVal.setHeaderType(typeFlag);//headertype 0 = normal
retVal.setGranulePosition(lastGran);
retVal.setBitstreamSerialNumber(trackInf[DTSCID].OGGSerial);
retVal.setPageSequenceNumber(trackInf[DTSCID].seqNum);
retVal.setSegmentTable(curSegTable);
retVal.setPayload((char*)dataBuffer.c_str(), dataBuffer.size());
retVal.setCRCChecksum(retVal.calcChecksum());
trackInf[DTSCID].seqNum++;
pageBuffer += std::string((char*)retVal.getPage(), retVal.getPageSize());
}
return pageBuffer;
}
}

29
src/converters/oggconv.h Normal file
View file

@ -0,0 +1,29 @@
#include <mist/ogg.h>
namespace OGG {
struct trackStats{
unsigned int OGGSerial;
unsigned int seqNum;
std::string codec;
//theora vars
unsigned int lastKeyFrame;
unsigned int sinceKeyFrame;
unsigned int significantValue;//KFGShift for theora and other video;
int prevBlockFlag;
//vorbis vars
bool hadFirst;
std::deque<vorbis::mode> vorbisModes;//modes for vorbis
char blockSize[2];
};
class converter{
public:
void readDTSCHeader(DTSC::Meta & meta);
std::string readDTSCVector(std::vector <JSON::Value> DTSCVec);
std::string parsedPages;
private:
std::map <long long unsigned int, trackStats> trackInf;
//long long unsigned int calcGranule(long long unsigned int trackID, bool keyFrame);
};
}