Example #1
0
 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());
 }
Example #2
0
 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());
 }
Example #3
0
  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;
  }
Example #4
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
Example #5
0
void DTSC::File::writePacket(JSON::Value & newPacket) {
  writePacket(newPacket.toNetPacked());
}