213 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #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["friendly"] = "OGG over HTTP";
 | |
|     capa["desc"] = "Pseudostreaming in OGG format over HTTP";
 | |
|     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"] = 8;
 | |
|     capa["methods"][0u]["nolive"] = 1;
 | |
|   }
 | |
| 
 | |
|   void OutProgressiveOGG::sendNext(){
 | |
|     unsigned int track = thisPacket.getTrackId();
 | |
| 
 | |
| 
 | |
|     OGG::oggSegment newSegment;
 | |
|     thisPacket.getString("data", newSegment.dataString);
 | |
|     pageBuffer[track].totalFrames = ((double)thisPacket.getTime() / (1000000.0f / myMeta.tracks[track].fpks)) + 1.5; //should start at 1. added .5 for rounding.
 | |
| 
 | |
|     if (pageBuffer[track].codec == OGG::THEORA){
 | |
|       newSegment.isKeyframe = thisPacket.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
 | |
|         pageBuffer[track].lastKeyFrame = pageBuffer[track].totalFrames;
 | |
|       }
 | |
|       newSegment.framesSinceKeyFrame = pageBuffer[track].totalFrames - pageBuffer[track].lastKeyFrame;
 | |
|       newSegment.lastKeyFrameSeen = pageBuffer[track].lastKeyFrame;
 | |
|     }
 | |
| 
 | |
|     newSegment.frameNumber = pageBuffer[track].totalFrames;
 | |
|     newSegment.timeStamp = thisPacket.getTime();
 | |
| 
 | |
|     pageBuffer[track].oggSegments.push_back(newSegment);
 | |
| 
 | |
|     if (pageBuffer[track].codec == OGG::VORBIS){
 | |
|       pageBuffer[track].vorbisStuff();//this updates lastKeyFrame
 | |
|     }
 | |
|     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++){
 | |
|       if (myMeta.tracks[*it].codec == "theora"){ //get size and position of init data for this page.
 | |
|         parseInit(myMeta.tracks[*it].init,  initData[*it]);
 | |
|         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"){
 | |
|         parseInit(myMeta.tracks[*it].init,  initData[*it]);
 | |
|         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;
 | |
|         initData[*it].push_back(myMeta.tracks[*it].init);
 | |
|         initData[*it].push_back(std::string("OpusTags\000\000\000\012MistServer\000\000\000\000", 26));
 | |
|       }
 | |
|       pageBuffer[*it].clear(OGG::BeginOfStream, 0, *it, 0);   //CREATES a (map)pageBuffer object, *it = id, pagetype=BOS
 | |
|       newSegment.dataString = initData[*it].front();
 | |
|       initData[*it].pop_front();
 | |
|       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++){
 | |
|       while (initData[*it].size()){
 | |
|         newSegment.dataString = initData[*it].front();
 | |
|         initData[*it].pop_front();
 | |
|         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.method == "OPTIONS" || HTTP_R.method == "HEAD"){
 | |
|         HTTP_S.Clean();
 | |
|         HTTP_S.SetHeader("Content-Type", "video/ogg");
 | |
|         HTTP_S.protocol = "HTTP/1.0";
 | |
|         HTTP_S.SendResponse("200", "OK", myConn);
 | |
|         HTTP_S.Clean();
 | |
|         return;
 | |
|       }
 | |
|       
 | |
|       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();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | 
