Example #1
0
 ///\brief Sends a RTMP command either in AMF or AMF3 mode.
 ///\param amfReply The data to be sent over RTMP.
 ///\param messageType The type of message.
 ///\param streamId The ID of the AMF stream.
 void sendCommand(AMF::Object & amfReply, int messageType, int streamId){
 #if DEBUG >= 8
   std::cerr << amfReply.Print() << std::endl;
 #endif
   if (messageType == 17){
     Socket.SendNow(RTMPStream::SendChunk(3, messageType, streamId, (char)0 + amfReply.Pack()));
   }else{
     Socket.SendNow(RTMPStream::SendChunk(3, messageType, streamId, amfReply.Pack()));
   }
 } //sendCommand
Example #2
0
void Connector_RTMP::sendCommand(AMF::Object & amfreply, int messagetype, int stream_id){
  #if DEBUG >= 4
  std::cerr << amfreply.Print() << std::endl;
  #endif
  if (messagetype == 17){
    Socket.SendNow(RTMPStream::SendChunk(3, messagetype, stream_id, (char)0+amfreply.Pack()));
  }else{
    Socket.SendNow(RTMPStream::SendChunk(3, messagetype, stream_id, amfreply.Pack()));
  }
}//sendCommand
Example #3
0
 ///\brief Debugging tool for AMF data.
 ///
 /// Expects AMF data through stdin, outputs human-readable information to stderr.
 ///\return The return code of the analyser.
 int analyseAMF(){
   std::string amfBuffer;
   //Read all of std::cin to amfBuffer
   while (std::cin.good()){
     amfBuffer += std::cin.get();
   }
   //Strip the invalid last character
   amfBuffer.erase((amfBuffer.end() - 1));
   //Parse into an AMF::Object
   AMF::Object amfData = AMF::parse(amfBuffer);
   //Print the output.
   std::cerr << amfData.Print() << std::endl;
   return 0;
 }
Example #4
0
/// Returns a std::string describing the tag in detail.
/// The string includes information about whether the tag is
/// audio, video or metadata, what encoding is used, and the details
/// of the encoding itself.
std::string FLV::Tag::tagType() {
  std::stringstream R;
  R << len << " bytes of ";
  switch (data[0]) {
    case 0x09:
      R << getVideoCodec() << " video ";
      switch (data[11] & 0xF0) {
        case 0x10:
          R << "keyframe";
          break;
        case 0x20:
          R << "iframe";
          break;
        case 0x30:
          R << "disposableiframe";
          break;
        case 0x40:
          R << "generatedkeyframe";
          break;
        case 0x50:
          R << "videoinfo";
          break;
      }
      if ((data[11] & 0x0F) == 7) {
        switch (data[12]) {
          case 0:
            R << " header";
            break;
          case 1:
            R << " NALU";
            break;
          case 2:
            R << " endofsequence";
            break;
        }
      }
      break;
    case 0x08:
      R << getAudioCodec();
      switch (data[11] & 0x0C) {
        case 0x0:
          R << " 5.5kHz";
          break;
        case 0x4:
          R << " 11kHz";
          break;
        case 0x8:
          R << " 22kHz";
          break;
        case 0xC:
          R << " 44kHz";
          break;
      }
      switch (data[11] & 0x02) {
        case 0:
          R << " 8bit";
          break;
        case 2:
          R << " 16bit";
          break;
      }
      switch (data[11] & 0x01) {
        case 0:
          R << " mono";
          break;
        case 1:
          R << " stereo";
          break;
      }
      R << " audio";
      if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)) {
        R << " initdata";
      }
      break;
    case 0x12: {
        R << "(meta)data: ";
        AMF::Object metadata = AMF::parse((unsigned char *)data + 11, len - 15);
        R << metadata.Print();
        break;
      }
    default:
      R << "unknown";
      break;
  }
  return R.str();
} //FLV::Tag::tagtype
Example #5
0
void Connector_RTMP::parseAMFCommand(AMF::Object & amfdata, int messagetype, int stream_id){
  #if DEBUG >= 4
  fprintf(stderr, "Received command: %s\n", amfdata.Print().c_str());
  #endif
  #if DEBUG >= 3
  fprintf(stderr, "AMF0 command: %s\n", amfdata.getContentP(0)->StrValue().c_str());
  #endif
  if (amfdata.getContentP(0)->StrValue() == "connect"){
    double objencoding = 0;
    if (amfdata.getContentP(2)->getContentP("objectEncoding")){
      objencoding = amfdata.getContentP(2)->getContentP("objectEncoding")->NumValue();
    }
    #if DEBUG >= 4
    int tmpint;
    if (amfdata.getContentP(2)->getContentP("videoCodecs")){
      tmpint = (int)amfdata.getContentP(2)->getContentP("videoCodecs")->NumValue();
      if (tmpint & 0x04){fprintf(stderr, "Sorensen video support detected\n");}
      if (tmpint & 0x80){fprintf(stderr, "H264 video support detected\n");}
    }
    if (amfdata.getContentP(2)->getContentP("audioCodecs")){
      tmpint = (int)amfdata.getContentP(2)->getContentP("audioCodecs")->NumValue();
      if (tmpint & 0x04){fprintf(stderr, "MP3 audio support detected\n");}
      if (tmpint & 0x400){fprintf(stderr, "AAC audio support detected\n");}
    }
    #endif
    RTMPStream::chunk_snd_max = 4096;
    Socket.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max));//send chunk size max (msg 1)
    Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size));//send window acknowledgement size (msg 5)
    Socket.Send(RTMPStream::SendCTL(6, RTMPStream::rec_window_size));//send rec window acknowledgement size (msg 6)
    Socket.Send(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
    //send a _result reply
    AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
    amfreply.addContent(AMF::Object("", "_result"));//result success
    amfreply.addContent(amfdata.getContent(1));//same transaction ID
    amfreply.addContent(AMF::Object(""));//server properties
    amfreply.getContentP(2)->addContent(AMF::Object("fmsVer", "FMS/3,5,5,2004"));
    amfreply.getContentP(2)->addContent(AMF::Object("capabilities", (double)31));
    amfreply.getContentP(2)->addContent(AMF::Object("mode", (double)1));
    amfreply.addContent(AMF::Object(""));//info
    amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
    amfreply.getContentP(3)->addContent(AMF::Object("code", "NetConnection.Connect.Success"));
    amfreply.getContentP(3)->addContent(AMF::Object("description", "Connection succeeded."));
    amfreply.getContentP(3)->addContent(AMF::Object("clientid", 1337));
    amfreply.getContentP(3)->addContent(AMF::Object("objectEncoding", objencoding));
    //amfreply.getContentP(3)->addContent(AMF::Object("data", AMF::AMF0_ECMA_ARRAY));
    //amfreply.getContentP(3)->getContentP(4)->addContent(AMF::Object("version", "3,5,4,1004"));
    sendCommand(amfreply, messagetype, stream_id);
    //send onBWDone packet - no clue what it is, but real server sends it...
    //amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER);
    //amfreply.addContent(AMF::Object("", "onBWDone"));//result
    //amfreply.addContent(amfdata.getContent(1));//same transaction ID
    //amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null
    //sendCommand(amfreply, messagetype, stream_id);
    return;
  }//connect
  if (amfdata.getContentP(0)->StrValue() == "createStream"){
    //send a _result reply
    AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
    amfreply.addContent(AMF::Object("", "_result"));//result success
    amfreply.addContent(amfdata.getContent(1));//same transaction ID
    amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
    amfreply.addContent(AMF::Object("", (double)1));//stream ID - we use 1
    sendCommand(amfreply, messagetype, stream_id);
    Socket.Send(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
    return;
  }//createStream
  if ((amfdata.getContentP(0)->StrValue() == "closeStream") || (amfdata.getContentP(0)->StrValue() == "deleteStream")){
    if (SS.connected()){SS.close();}
    return;
  }
  if ((amfdata.getContentP(0)->StrValue() == "getStreamLength") || (amfdata.getContentP(0)->StrValue() == "getMovLen")){
    //send a _result reply
    AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
    amfreply.addContent(AMF::Object("", "_result"));//result success
    amfreply.addContent(amfdata.getContent(1));//same transaction ID
    amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
    amfreply.addContent(AMF::Object("", (double)0));//zero length
    sendCommand(amfreply, messagetype, stream_id);
    return;
  }//getStreamLength
  if ((amfdata.getContentP(0)->StrValue() == "publish")){
    if (amfdata.getContentP(3)){
      streamname = amfdata.getContentP(3)->StrValue();
      /// \todo implement push for MistPlayer or restrict and change to getLive
      SS = Util::Stream::getStream(streamname);
      if (!SS.connected()){
        #if DEBUG >= 1
        fprintf(stderr, "Could not connect to server!\n");
        #endif
        Socket.close();//disconnect user
        return;
      }
      SS.Send("P ");
      SS.Send(Socket.getHost().c_str());
      SS.Send("\n");
      nostats = true;
      #if DEBUG >= 4
      fprintf(stderr, "Connected to buffer, starting to send data...\n");
      #endif
    }
    //send a _result reply
    AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
    amfreply.addContent(AMF::Object("", "_result"));//result success
    amfreply.addContent(amfdata.getContent(1));//same transaction ID
    amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
    amfreply.addContent(AMF::Object("", 1, AMF::AMF0_BOOL));//publish success?
    sendCommand(amfreply, messagetype, stream_id);
    Socket.Send(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
    //send a status reply
    amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER);
    amfreply.addContent(AMF::Object("", "onStatus"));//status reply
    amfreply.addContent(AMF::Object("", 0, AMF::AMF0_NUMBER));//same transaction ID
    amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
    amfreply.addContent(AMF::Object(""));//info
    amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
    amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Publish.Start"));
    amfreply.getContentP(3)->addContent(AMF::Object("description", "Stream is now published!"));
    amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
    sendCommand(amfreply, messagetype, stream_id);
    return;
  }//getStreamLength
  if (amfdata.getContentP(0)->StrValue() == "checkBandwidth"){
    //send a _result reply
    AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
    amfreply.addContent(AMF::Object("", "_result"));//result success
    amfreply.addContent(amfdata.getContent(1));//same transaction ID
    amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
    amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
    sendCommand(amfreply, messagetype, stream_id);
    return;
  }//checkBandwidth
  if ((amfdata.getContentP(0)->StrValue() == "play") || (amfdata.getContentP(0)->StrValue() == "play2")){
    //set reply number and stream name, actual reply is sent up in the SS.spool() handler
    play_trans = amfdata.getContentP(1)->NumValue();
    play_msgtype = messagetype;
    play_streamid = stream_id;
    streamname = amfdata.getContentP(3)->StrValue();
    Connector_RTMP::ready4data = true;//start sending video data!
    return;
  }//play
  if ((amfdata.getContentP(0)->StrValue() == "seek")){
    //set reply number and stream name, actual reply is sent up in the SS.spool() handler
    play_trans = amfdata.getContentP(1)->NumValue();
    play_msgtype = messagetype;
    play_streamid = stream_id;
    stream_inited = false;
    
    AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
    amfreply.addContent(AMF::Object("", "onStatus"));//status reply
    amfreply.addContent(amfdata.getContent(1));//same transaction ID
    amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
    amfreply.addContent(AMF::Object(""));//info
    amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
    amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Seek.Notify"));
    amfreply.getContentP(3)->addContent(AMF::Object("description", "Seeking to the specified time"));
    amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV"));
    amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
    sendCommand(amfreply, play_msgtype, play_streamid);
    SS.Send("s ");
    SS.Send(JSON::Value((long long int)amfdata.getContentP(3)->NumValue()).asString().c_str());
    SS.Send("\n");
    return;
  }//seek
  if ((amfdata.getContentP(0)->StrValue() == "pauseRaw") || (amfdata.getContentP(0)->StrValue() == "pause")){
    if (amfdata.getContentP(3)->NumValue()){
      SS.Send("q\n");//quit playing
      //send a status reply
      AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
      amfreply.addContent(AMF::Object("", "onStatus"));//status reply
      amfreply.addContent(amfdata.getContent(1));//same transaction ID
      amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
      amfreply.addContent(AMF::Object(""));//info
      amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
      amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Pause.Notify"));
      amfreply.getContentP(3)->addContent(AMF::Object("description", "Pausing playback"));
      amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV"));
      amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
      sendCommand(amfreply, play_msgtype, play_streamid);
    }else{
      SS.Send("p\n");//start playing
      //send a status reply
      AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
      amfreply.addContent(AMF::Object("", "onStatus"));//status reply
      amfreply.addContent(amfdata.getContent(1));//same transaction ID
      amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
      amfreply.addContent(AMF::Object(""));//info
      amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
      amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Unpause.Notify"));
      amfreply.getContentP(3)->addContent(AMF::Object("description", "Resuming playback"));
      amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV"));
      amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
      sendCommand(amfreply, play_msgtype, play_streamid);
    }
    return;
  }//seek
  
  #if DEBUG >= 2
  fprintf(stderr, "AMF0 command not processed! :(\n");
  #endif
}//parseAMFCommand