예제 #1
0
파일: dtsc.cpp 프로젝트: FihlaTV/mistserver
/// 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;
}
예제 #2
0
  ///\brief Main function for the TS Connector
  ///\param conn A socket describing the connection the client.
  ///\param streamName The stream to connect to.
  ///\return The exit code of the connector.
  int tsConnector(Socket::Connection conn, std::string streamName, std::string trackIDs){
    std::string ToPack;
    TS::Packet PackData;
    std::string DTMIData;
    int PacketNumber = 0;
    long long unsigned int TimeStamp = 0;
    int ThisNaluSize;
    char VideoCounter = 0;
    char AudioCounter = 0;
    bool WritePesHeader;
    bool IsKeyFrame;
    bool FirstKeyFrame = true;
    bool FirstIDRInKeyFrame;
    MP4::AVCC avccbox;
    bool haveAvcc = false;

    DTSC::Stream Strm;
    bool inited = false;
    Socket::Connection ss;

    while (conn.connected()){
      if ( !inited){
        ss = Util::Stream::getStream(streamName);
        if ( !ss.connected()){
  #if DEBUG >= 1
          fprintf(stderr, "Could not connect to server!\n");
  #endif
          conn.close();
          break;
        }

        if(trackIDs == ""){
          // no track ids given? Find the first video and first audio track (if available) and use those!
          int videoID = -1;
          int audioID = -1;

          Strm.waitForMeta(ss);
          
          if (Strm.metadata.isMember("tracks")){

            for (JSON::ObjIter trackIt = Strm.metadata["tracks"].ObjBegin(); trackIt != Strm.metadata["tracks"].ObjEnd(); trackIt++){

              if (audioID == -1 && trackIt->second["type"].asString() == "audio"){
                audioID = trackIt->second["trackid"].asInt();
                if( trackIDs != ""){
                  trackIDs += " " + trackIt->second["trackid"].asString();
                }else{
                    trackIDs = trackIt->second["trackid"].asString();
                }
              }

              if (videoID == -1 && trackIt->second["type"].asString() == "video"){
                videoID = trackIt->second["trackid"].asInt();
                if( trackIDs != ""){
                  trackIDs += " " + trackIt->second["trackid"].asString();
                }else{
                  trackIDs = trackIt->second["trackid"].asString();
                }
              }

            }	// for iterator
          } // if isMember("tracks")
        } // if trackIDs == ""

        std::string cmd = "t " + trackIDs + "\ns 0\np\n";
        ss.SendNow( cmd );
        inited = true;
      }
      if (ss.spool()){
        while (Strm.parsePacket(ss.Received())){

          std::stringstream TSBuf;
          Socket::Buffer ToPack;
          //write PAT and PMT TS packets
          if (PacketNumber == 0){
            PackData.DefaultPAT();
            TSBuf.write(PackData.ToString(), 188);
            PackData.DefaultPMT();
            TSBuf.write(PackData.ToString(), 188);
            PacketNumber += 2;
          }

          int PIDno = 0;
          char * ContCounter = 0;
          if (Strm.lastType() == DTSC::VIDEO){
		      if ( !haveAvcc){
		          avccbox.setPayload(Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["init"].asString());
		        haveAvcc = true;
		      }

            IsKeyFrame = Strm.getPacket().isMember("keyframe");
            if (IsKeyFrame){
              TimeStamp = (Strm.getPacket()["time"].asInt() * 27000);
            }
            ToPack.append(avccbox.asAnnexB());
                while (Strm.lastData().size() > 4){
              ThisNaluSize = (Strm.lastData()[0] << 24) + (Strm.lastData()[1] << 16) + (Strm.lastData()[2] << 8) + Strm.lastData()[3];
              Strm.lastData().replace(0, 4, TS::NalHeader, 4);
              if (ThisNaluSize + 4 == Strm.lastData().size()){
                ToPack.append(Strm.lastData());
                break;
              }else{
                ToPack.append(Strm.lastData().c_str(), ThisNaluSize + 4);
                Strm.lastData().erase(0, ThisNaluSize + 4);
              }
            }
            ToPack.prepend(TS::Packet::getPESVideoLeadIn(0ul, Strm.getPacket()["time"].asInt() * 90));
            PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
            ContCounter = &VideoCounter;
          }else if (Strm.lastType() == DTSC::AUDIO){
                ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["init"].asString()));
            ToPack.append(Strm.lastData());
            ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
            PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
            ContCounter = &AudioCounter;
                IsKeyFrame = false;
          }

          //initial packet
          PackData.Clear();
          PackData.PID(PIDno);
          PackData.ContinuityCounter(( *ContCounter)++);
          PackData.UnitStart(1);
          if (IsKeyFrame){
            PackData.RandomAccess(1);
            PackData.PCR(TimeStamp);
          }
          unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184));
          std::string gonnaSend = ToPack.remove(toSend);
          PackData.FillFree(gonnaSend);
          TSBuf.write(PackData.ToString(), 188);
          PacketNumber++;

          //rest of packets
          while (ToPack.size()){
            PackData.Clear();
            PackData.PID(PIDno);
            PackData.ContinuityCounter(( *ContCounter)++);
            toSend = PackData.AddStuffing(ToPack.bytes(184));
            gonnaSend = ToPack.remove(toSend);
            PackData.FillFree(gonnaSend);
            TSBuf.write(PackData.ToString(), 188);
            PacketNumber++;
          }

          TSBuf.flush();
          if (TSBuf.str().size()){
            conn.SendNow(TSBuf.str().c_str(), TSBuf.str().size());
            TSBuf.str("");
          }
          TSBuf.str("");
          PacketNumber = 0;
        }
      }else{
        Util::sleep(1000);
        conn.spool();
      }
    }
    return 0;
  }
예제 #3
0
  ///\brief Main function for the TS Connector
  ///\param conn A socket describing the connection the client.
  ///\param streamName The stream to connect to.
  ///\return The exit code of the connector.
  int tsConnector(Socket::Connection conn, std::string streamName){
    std::string ToPack;
    TS::Packet PackData;
    std::string DTMIData;
    int PacketNumber = 0;
    long long unsigned int TimeStamp = 0;
    int ThisNaluSize;
    char VideoCounter = 0;
    char AudioCounter = 0;
    bool WritePesHeader;
    bool IsKeyFrame;
    bool FirstKeyFrame = true;
    bool FirstIDRInKeyFrame;
    MP4::AVCC avccbox;
    bool haveAvcc = false;

    DTSC::Stream Strm;
    bool inited = false;
    Socket::Connection ss;

    while (conn.connected()){
      if ( !inited){
        ss = Util::Stream::getStream(streamName);
        if ( !ss.connected()){
  #if DEBUG >= 1
          fprintf(stderr, "Could not connect to server!\n");
  #endif
          conn.close();
          break;
        }
        ss.SendNow("p\n");
        inited = true;
      }
      if (ss.spool()){
        while (Strm.parsePacket(ss.Received())){
          if ( !haveAvcc){
            avccbox.setPayload(Strm.metadata["video"]["init"].asString());
            haveAvcc = true;
          }
          std::stringstream TSBuf;
          Socket::Buffer ToPack;
          //write PAT and PMT TS packets
          if (PacketNumber == 0){
            PackData.DefaultPAT();
            TSBuf.write(PackData.ToString(), 188);
            PackData.DefaultPMT();
            TSBuf.write(PackData.ToString(), 188);
            PacketNumber += 2;
          }

          int PIDno = 0;
          char * ContCounter = 0;
          if (Strm.lastType() == DTSC::VIDEO){
            IsKeyFrame = Strm.getPacket(0).isMember("keyframe");
            if (IsKeyFrame){
              TimeStamp = (Strm.getPacket(0)["time"].asInt() * 27000);
            }
            ToPack.append(avccbox.asAnnexB());
            while (Strm.lastData().size()){
              ThisNaluSize = (Strm.lastData()[0] << 24) + (Strm.lastData()[1] << 16) + (Strm.lastData()[2] << 8) + Strm.lastData()[3];
              Strm.lastData().replace(0, 4, TS::NalHeader, 4);
              if (ThisNaluSize + 4 == Strm.lastData().size()){
                ToPack.append(Strm.lastData());
                break;
              }else{
                ToPack.append(Strm.lastData().c_str(), ThisNaluSize + 4);
                Strm.lastData().erase(0, ThisNaluSize + 4);
              }
            }
            ToPack.prepend(TS::Packet::getPESVideoLeadIn(0ul, Strm.getPacket(0)["time"].asInt() * 90));
            PIDno = 0x100;
            ContCounter = &VideoCounter;
          }else if (Strm.lastType() == DTSC::AUDIO){
            ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata["audio"]["init"].asString()));
            ToPack.append(Strm.lastData());
            ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket(0)["time"].asInt() * 90));
            PIDno = 0x101;
            ContCounter = &AudioCounter;
          }

          //initial packet
          PackData.Clear();
          PackData.PID(PIDno);
          PackData.ContinuityCounter(( *ContCounter)++);
          PackData.UnitStart(1);
          if (IsKeyFrame){
            PackData.RandomAccess(1);
            PackData.PCR(TimeStamp);
          }
          unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184));
          std::string gonnaSend = ToPack.remove(toSend);
          PackData.FillFree(gonnaSend);
          TSBuf.write(PackData.ToString(), 188);
          PacketNumber++;

          //rest of packets
          while (ToPack.size()){
            PackData.Clear();
            PackData.PID(PIDno);
            PackData.ContinuityCounter(( *ContCounter)++);
            toSend = PackData.AddStuffing(ToPack.bytes(184));
            gonnaSend = ToPack.remove(toSend);
            PackData.FillFree(gonnaSend);
            TSBuf.write(PackData.ToString(), 188);
            PacketNumber++;
          }

          TSBuf.flush();
          if (TSBuf.str().size()){
            conn.SendNow(TSBuf.str().c_str(), TSBuf.str().size());
            TSBuf.str("");
          }
          TSBuf.str("");
          PacketNumber = 0;
        }
      }
    }
    return 0;
  }
예제 #4
0
 void OutTS::sendNext(){
   Socket::Buffer ToPack;
   char * ContCounter = 0;
   bool IsKeyFrame = false;
   
   char * dataPointer = 0;
   int dataLen = 0;
   currentPacket.getString("data", dataPointer, dataLen);
   
   //detect packet type, and put converted data into ToPack.
   if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){
     ToPack.append(TS::Packet::getPESVideoLeadIn(0ul, currentPacket.getTime() * 90));
     
     IsKeyFrame = currentPacket.getInt("keyframe");
     if (IsKeyFrame){
       if (!haveAvcc){
         avccbox.setPayload(myMeta.tracks[currentPacket.getTrackId()].init);
         haveAvcc = true;
       }
       ToPack.append(avccbox.asAnnexB());
     }
     unsigned int i = 0;
     while (i + 4 < (unsigned int)dataLen){
       unsigned int ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3];
       if (ThisNaluSize + i + 4 > (unsigned int)dataLen){
         DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen);
         break;
       }
       ToPack.append("\000\000\000\001", 4);
       i += 4;
       ToPack.append(dataPointer + i, ThisNaluSize);
       i += ThisNaluSize;
     }
     ContCounter = &VideoCounter;
   }else if (myMeta.tracks[currentPacket.getTrackId()].type == "audio"){
     ToPack.append(TS::Packet::getPESAudioLeadIn(7+dataLen, currentPacket.getTime() * 90));
     ToPack.append(TS::GetAudioHeader(dataLen, myMeta.tracks[currentPacket.getTrackId()].init));
     ToPack.append(dataPointer, dataLen);
     ContCounter = &AudioCounter;
   }
   
   bool first = true;
   //send TS packets
   while (ToPack.size()){
     PackData.Clear();
     /// \todo Update according to sendHeader()'s generated data.
     //0x100 - 1 + currentPacket.getTrackId()
     if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){
       PackData.PID(0x100);
     }else{
       PackData.PID(0x101);
     }
     PackData.ContinuityCounter((*ContCounter)++);
     if (first){
       PackData.UnitStart(1);
       if (IsKeyFrame){
         PackData.RandomAccess(1);
         PackData.PCR(currentPacket.getTime() * 27000);
       }
       first = false;
     }
     unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184));
     std::string gonnaSend = ToPack.remove(toSend);
     PackData.FillFree(gonnaSend);
     myConn.SendNow(PackData.ToString(), 188);
   }
 }
예제 #5
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