void inputFLV::getNext(bool smart) { static JSON::Value thisPack; static AMF::Object amf_storage; thisPack.null(); long long int lastBytePos = ftell(inFile); FLV::Tag tmpTag; while (!feof(inFile) && !FLV::Parse_Error){ if (tmpTag.FileLoader(inFile)){ thisPack = tmpTag.toJSON(myMeta, amf_storage); thisPack["bpos"] = lastBytePos; if ( !selectedTracks.count(thisPack["trackid"].asInt())){ getNext(); } break; } } if (FLV::Parse_Error){ FAIL_MSG("FLV error: %s", FLV::Error_Str.c_str()); thisPack.null(); thisPacket.null(); return; } std::string tmpStr = thisPack.toNetPacked(); thisPacket.reInit(tmpStr.data(), tmpStr.size()); }
void inputFLV::getNext(bool smart) { static JSON::Value thisPack; thisPack.null(); long long int lastBytePos = ftell(inFile); FLV::Tag tmpTag; while (!feof(inFile) && !FLV::Parse_Error){ if (tmpTag.FileLoader(inFile)){ thisPack = tmpTag.toJSON(myMeta); thisPack["bpos"] = lastBytePos; if ( !selectedTracks.count(thisPack["trackid"].asInt())){ getNext(); } break; } } if (FLV::Parse_Error){ std::cerr << FLV::Error_Str << std::endl; thisPack.null(); lastPack.null(); return; } std::string tmpStr = thisPack.toNetPacked(); lastPack.reInit(tmpStr.data(), tmpStr.size()); }
int OGG2DTSC(){ std::string oggBuffer; OGG::Page oggPage; //Read all of std::cin to oggBuffer double mspft = 0;//microseconds per frame double mspfv = 0;//microseconds per frame vorbis JSON::Value DTSCOut; JSON::Value DTSCHeader; DTSCHeader.null(); DTSCHeader["moreheader"] = 0ll; std::map<long unsigned int, oggTrack> trackData; long long int lastTrackID = 1; int headerSeen = 0; bool headerWritten = false;//important bool, used for outputting the simple DTSC header. //while stream busy while (std::cin.good()){ for (unsigned int i = 0; (i < 1024) && (std::cin.good()); i++){//buffering oggBuffer += std::cin.get(); } while (oggPage.read(oggBuffer)){//reading ogg to ogg::page //on succes, we handle one page long unsigned int sNum = oggPage.getBitstreamSerialNumber(); if (oggPage.typeBOS()){//defines a new track if (memcmp(oggPage.getFullPayload()+1, "theora", 6) == 0){ headerSeen += 1; headerWritten = false; trackData[sNum].codec = THEORA; //fix timerate here //frn/frd = fps theora::header tempHead; tempHead.read(oggPage.getFullPayload(), oggPage.getPayloadSize()); mspft = (double)(tempHead.getFRD() * 1000) / tempHead.getFRN(); }else if(memcmp(oggPage.getFullPayload()+1, "vorbis", 6) == 0){ headerSeen += 1; headerWritten = false; trackData[sNum].codec = VORBIS; vorbis::header tempHead; tempHead.read(oggPage.getFullPayload(), oggPage.getPayloadSize()); mspfv = (double)1000 / ntohl(tempHead.getAudioSampleRate()); }else{ std::cerr << "Unknown Codec, " << std::string(oggPage.getFullPayload()+1, 6)<<" skipping" << std::endl; continue; } trackData[sNum].dtscID = lastTrackID++; std::stringstream tID; tID << "track" << trackData[sNum].dtscID; trackData[sNum].name = tID.str(); } //if Serial number is available in mapping if(trackData.find(sNum)!=trackData.end()){//create DTSC from OGG page int offset = 0; for (std::deque<unsigned int>::iterator it = oggPage.getSegmentTableDeque().begin(); it != oggPage.getSegmentTableDeque().end(); it++){ if (trackData[sNum].parsedHeaders){ //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{ //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["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 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(); } }else{//if we ouput a header: //switch on codec switch(trackData[sNum].codec){ case THEORA:{ theora::header tHead; if(tHead.read(oggPage.getFullPayload()+offset, (*it))){//if the current segment is a Theora header part //fillDTSC header switch(tHead.getHeaderType()){ case 0:{ //identification header trackData[sNum].idHeader = tHead; DTSCHeader["tracks"][trackData[sNum].name]["height"] = (long long)tHead.getPICH(); DTSCHeader["tracks"][trackData[sNum].name]["width"] = (long long)tHead.getPICW(); DTSCHeader["tracks"][trackData[sNum].name]["idheader"] = std::string(oggPage.getFullPayload()+offset, (*it)); break; } case 1: //comment header DTSCHeader["tracks"][trackData[sNum].name]["commentheader"] = std::string(oggPage.getFullPayload()+offset, (*it)); break; case 2:{ //setup header, also the point to start writing the header DTSCHeader["tracks"][trackData[sNum].name]["codec"] = "theora"; DTSCHeader["tracks"][trackData[sNum].name]["trackid"] = (long long)trackData[sNum].dtscID; DTSCHeader["tracks"][trackData[sNum].name]["type"] = "video"; DTSCHeader["tracks"][trackData[sNum].name]["init"] = std::string(oggPage.getFullPayload()+offset, (*it)); headerSeen --; trackData[sNum].parsedHeaders = true; trackData[sNum].lastGran = 0; break; } } } break; } case VORBIS:{ vorbis::header vHead; if(vHead.read(oggPage.getFullPayload()+offset, (*it))){//if the current segment is a Vorbis header part switch(vHead.getHeaderType()){ 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:{ DTSCHeader["tracks"][trackData[sNum].name]["commentheader"] = std::string(oggPage.getFullPayload()+offset, (*it)); break; } case 5:{ DTSCHeader["tracks"][trackData[sNum].name]["codec"] = "vorbis"; 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; } default:{ std::cerr << "Unsupported header type for vorbis" << std::endl; } } }else{ std::cerr << "Unknown Header" << std::endl; } break; } default: std::cerr << "Can not handle this codec" << std::endl; break; } } offset += (*it); } }else{ std::cerr <<"Error! Unknown bitstream number " << oggPage.getBitstreamSerialNumber() << std::endl; } //write header here if (headerSeen == 0 && headerWritten == false){ std::cout << DTSCHeader.toNetPacked(); headerWritten = true; } //write section if (oggPage.typeEOS()){//ending page //remove from trackdata trackData.erase(sNum); } } } std::cerr << "DTSC file created succesfully" << std::endl; return 0; }
/// Tries to get and parse one RTMP chunk at a time. void Connector_RTMP::parseChunk(Socket::Buffer & inbuffer){ //for DTSC conversion static JSON::Value meta_out; static std::stringstream prebuffer; // Temporary buffer before sending real data static bool sending = false; static unsigned int counter = 0; //for chunk parsing static RTMPStream::Chunk next; FLV::Tag F; static AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER); static AMF::Object amfelem("empty", AMF::AMF0_DDV_CONTAINER); static AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER); static AMF::Object3 amf3elem("empty", AMF::AMF3_DDV_CONTAINER); while (next.Parse(inbuffer)){ //send ACK if we received a whole window if ((RTMPStream::rec_cnt - RTMPStream::rec_window_at > RTMPStream::rec_window_size)){ RTMPStream::rec_window_at = RTMPStream::rec_cnt; Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt));//send ack (msg 3) } switch (next.msg_type_id){ case 0://does not exist #if DEBUG >= 2 fprintf(stderr, "UNKN: Received a zero-type message. This is an error.\n"); #endif break;//happens when connection breaks unexpectedly case 1://set chunk size RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str()); #if DEBUG >= 4 fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max); #endif break; case 2://abort message - we ignore this one #if DEBUG >= 4 fprintf(stderr, "CTRL: Abort message\n"); #endif //4 bytes of stream id to drop break; case 3://ack #if DEBUG >= 4 fprintf(stderr, "CTRL: Acknowledgement\n"); #endif RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str()); RTMPStream::snd_window_at = RTMPStream::snd_cnt; break; case 4:{ //2 bytes event type, rest = event data //types: //0 = stream begin, 4 bytes ID //1 = stream EOF, 4 bytes ID //2 = stream dry, 4 bytes ID //3 = setbufferlen, 4 bytes ID, 4 bytes length //4 = streamisrecorded, 4 bytes ID //6 = pingrequest, 4 bytes data //7 = pingresponse, 4 bytes data //we don't need to process this #if DEBUG >= 4 short int ucmtype = ntohs(*(short int*)next.data.c_str()); switch (ucmtype){ case 0: fprintf(stderr, "CTRL: UCM StreamBegin %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; case 1: fprintf(stderr, "CTRL: UCM StreamEOF %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; case 2: fprintf(stderr, "CTRL: UCM StreamDry %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; case 3: fprintf(stderr, "CTRL: UCM SetBufferLength %i %i\n", ntohl(*((int*)(next.data.c_str()+2))), ntohl(*((int*)(next.data.c_str()+6)))); break; case 4: fprintf(stderr, "CTRL: UCM StreamIsRecorded %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; case 6: fprintf(stderr, "CTRL: UCM PingRequest %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; case 7: fprintf(stderr, "CTRL: UCM PingResponse %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; default: fprintf(stderr, "CTRL: UCM Unknown (%hi)\n", ucmtype); break; } #endif } break; case 5://window size of other end #if DEBUG >= 4 fprintf(stderr, "CTRL: Window size\n"); #endif RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str()); RTMPStream::rec_window_at = RTMPStream::rec_cnt; Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt));//send ack (msg 3) break; case 6: #if DEBUG >= 4 fprintf(stderr, "CTRL: Set peer bandwidth\n"); #endif //4 bytes window size, 1 byte limit type (ignored) RTMPStream::snd_window_size = ntohl(*(int*)next.data.c_str()); Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size));//send window acknowledgement size (msg 5) break; case 8://audio data case 9://video data case 18://meta data if (SS.connected()){ F.ChunkLoader(next); JSON::Value pack_out = F.toJSON(meta_out); if (!pack_out.isNull()){ if (!sending){ counter++; if (counter > 8){ sending = true; SS.SendNow(meta_out.toNetPacked()); SS.SendNow(prebuffer.str().c_str());//write buffer prebuffer.str("");//clear buffer SS.Send(pack_out.toNetPacked()); }else{ prebuffer << pack_out.toNetPacked(); } }else{ SS.SendNow(pack_out.toNetPacked()); } } }else{ #if DEBUG >= 4 fprintf(stderr, "Received useless media data\n"); #endif Socket.close(); } break; case 15: #if DEBUG >= 4 fprintf(stderr, "Received AFM3 data message\n"); #endif break; case 16: #if DEBUG >= 4 fprintf(stderr, "Received AFM3 shared object\n"); #endif break; case 17:{ #if DEBUG >= 4 fprintf(stderr, "Received AFM3 command message\n"); #endif if (next.data[0] != 0){ next.data = next.data.substr(1); amf3data = AMF::parse3(next.data); #if DEBUG >= 4 amf3data.Print(); #endif }else{ #if DEBUG >= 4 fprintf(stderr, "Received AFM3-0 command message\n"); #endif next.data = next.data.substr(1); amfdata = AMF::parse(next.data); parseAMFCommand(amfdata, 17, next.msg_stream_id); }//parsing AMF0-style } break; case 19: #if DEBUG >= 4 fprintf(stderr, "Received AFM0 shared object\n"); #endif break; case 20:{//AMF0 command message amfdata = AMF::parse(next.data); parseAMFCommand(amfdata, 20, next.msg_stream_id); } break; case 22: #if DEBUG >= 4 fprintf(stderr, "Received aggregate message\n"); #endif break; default: #if DEBUG >= 1 fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n"); #endif Connector_RTMP::stopparsing = true; break; } } }//parseChunk
void DTSC::File::writePacket(JSON::Value & newPacket) { writePacket(newPacket.toNetPacked()); }