diff --git a/src/connectors/conn_http_progressive_ogg.cpp b/src/connectors/conn_http_progressive_ogg.cpp index 9867914e..b85372a1 100644 --- a/src/connectors/conn_http_progressive_ogg.cpp +++ b/src/connectors/conn_http_progressive_ogg.cpp @@ -20,6 +20,7 @@ #include #include #include +#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 > DTSCBuffer; std::set sendReady; //std::map 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(); diff --git a/src/converters/dtsc2ogg.cpp b/src/converters/dtsc2ogg.cpp index 1bba1ce8..1cd84304 100644 --- a/src/converters/dtsc2ogg.cpp +++ b/src/converters/dtsc2ogg.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "oggconv.h" #include #include @@ -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 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::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 >::iterator it = DTSCBuffer.begin(); - it != DTSCBuffer.end(); - it++ - ){ + for (std::map< long long int, std::vector >::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; } } diff --git a/src/converters/ogg2dtsc.cpp b/src/converters/ogg2dtsc.cpp index eb251ca3..d68968b9 100644 --- a/src/converters/ogg2dtsc.cpp +++ b/src/converters/ogg2dtsc.cpp @@ -10,6 +10,7 @@ #include #include #include +#include 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 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::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; diff --git a/src/converters/oggconv.cpp b/src/converters/oggconv.cpp new file mode 100644 index 00000000..ff778829 --- /dev/null +++ b/src/converters/oggconv.cpp @@ -0,0 +1,199 @@ +#include"oggconv.h" +#include +#include + + +namespace OGG{ + void converter::readDTSCHeader(DTSC::Meta & meta){ + //pages.clear(); + parsedPages = ""; + Page curOggPage; + srand (Util::getMS());//randomising with milliseconds from boot + std::vector curSegTable; + //trackInf.clear(); + //Creating ID headers for theora and vorbis + for ( std::map::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::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 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 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; + } +} diff --git a/src/converters/oggconv.h b/src/converters/oggconv.h new file mode 100644 index 00000000..ca9fa3d7 --- /dev/null +++ b/src/converters/oggconv.h @@ -0,0 +1,29 @@ +#include + +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 vorbisModes;//modes for vorbis + char blockSize[2]; + }; + + class converter{ + public: + void readDTSCHeader(DTSC::Meta & meta); + std::string readDTSCVector(std::vector DTSCVec); + std::string parsedPages; + private: + std::map trackInf; + //long long unsigned int calcGranule(long long unsigned int trackID, bool keyFrame); + }; + +}