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()); }
bool inputFLV::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 FLV data fseek(inFile, 13, SEEK_SET); FLV::Tag tmpTag; long long int lastBytePos = 13; while (!feof(inFile) && !FLV::Parse_Error){ if (tmpTag.FileLoader(inFile)){ lastPack.null(); lastPack = tmpTag.toJSON(myMeta); lastPack["bpos"] = lastBytePos; myMeta.update(lastPack); lastBytePos = ftell(inFile); } } if (FLV::Parse_Error){ std::cerr << FLV::Error_Str << std::endl; return false; } std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str()); oFile << myMeta.toJSON().toNetPacked(); oFile.close(); return true; }
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()); }
/// 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