Exemplo n.º 1
0
/// Attempts to parse a packet from the given Socket::Buffer.
/// Returns true if successful, removing the parsed part from the buffer.
/// Returns false if invalid or not enough data is in the buffer.
/// \arg buffer The Socket::Buffer to attempt to parse.
bool DTSC::Stream::parsePacket(Socket::Buffer & buffer) {
  uint32_t len;
  static bool syncing = false;
  if (buffer.available(8)) {
    std::string header_bytes = buffer.copy(8);
    if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0) {
      len = ntohl(((uint32_t *)header_bytes.c_str())[1]);
      if (!buffer.available(len + 8)) {
        return false;
      }
      unsigned int i = 0;
      std::string wholepacket = buffer.remove(len + 8);
      JSON::Value meta;
      JSON::fromDTMI((unsigned char *)wholepacket.c_str() + 8, len, i, meta);
      addMeta(meta);
      //recursively calls itself until failure or data packet instead of header
      return parsePacket(buffer);
    }
    int version = 0;
    if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0) {
      version = 1;
    }
    if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet2, 4) == 0) {
      version = 2;
    }
    if (version) {
      len = ntohl(((uint32_t *)header_bytes.c_str())[1]);
      if (!buffer.available(len + 8)) {
        return false;
      }
      JSON::Value newPack;
      unsigned int i = 0;
      std::string wholepacket = buffer.remove(len + 8);
      if (version == 1) {
        JSON::fromDTMI((unsigned char *)wholepacket.c_str() + 8, len, i, newPack);
      }
      if (version == 2) {
        JSON::fromDTMI2((unsigned char *)wholepacket.c_str() + 8, len, i, newPack);
      }
      addPacket(newPack);
      syncing = false;
      return true;
    }
#if DEBUG >= DLVL_WARN
    if (!syncing) {
      DEBUG_MSG(DLVL_WARN, "Invalid DTMI data detected - syncing");
      syncing = true;
    }
#endif
    buffer.get().clear();
  }
  return false;
}
Exemplo n.º 2
0
  ///\brief Gets and parses one RTMP chunk at a time.
  ///\param inputBuffer A buffer filled with chunk data.
  void parseChunk(Socket::Buffer & inputBuffer){
    //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;
    static 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(inputBuffer)){

      //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. Possible data corruption? Aborting!\n");
  #endif
          while (inputBuffer.size()){
            inputBuffer.get().clear();
          }
          ss.close();
          Socket.close();
          break; //happens when connection breaks unexpectedly
        case 1: //set chunk size
          RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str());
  #if DEBUG >= 5
          fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max);
  #endif
          break;
        case 2: //abort message - we ignore this one
  #if DEBUG >= 5
          fprintf(stderr, "CTRL: Abort message\n");
  #endif
          //4 bytes of stream id to drop
          break;
        case 3: //ack
  #if DEBUG >= 8
          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 >= 5
          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 >= 5
          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 >= 5
          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()){
            if (streamReset){
              //reset push data to empty, in case stream properties change
              meta_out.null();
              prebuffer.str("");
              sending = false;
              counter = 0;
              streamReset = false;
            }
            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(), prebuffer.str().size()); //write buffer
                  prebuffer.str(""); //clear buffer
                  ss.SendNow(pack_out.toNetPacked());
                }else{
                  prebuffer << pack_out.toNetPacked();
                }
              }else{
                ss.SendNow(pack_out.toNetPacked());
              }
            }
          }else{
  #if DEBUG >= 5
            fprintf(stderr, "Received useless media data\n");
  #endif
            Socket.close();
          }
          break;
        case 15:
  #if DEBUG >= 5
          fprintf(stderr, "Received AFM3 data message\n");
  #endif
          break;
        case 16:
  #if DEBUG >= 5
          fprintf(stderr, "Received AFM3 shared object\n");
  #endif
          break;
        case 17: {
  #if DEBUG >= 5
          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 >= 5
            amf3data.Print();
  #endif
          }else{
  #if DEBUG >= 5
            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 >= 5
          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 >= 5
          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
          stopParsing = true;
          break;
      }
    }
  } //parseChunk