Example #1
0
  /**
   * Decodes packet header.
   *
   * @param in Input IoBuffer
   * @param lastHeader Previous header
   * @return Decoded header
   */
  public static Header decodeHeader(IoBuffer in, Header lastHeader) {
    if (log.isTraceEnabled()) {
      log.trace("decodeHeader - lastHeader: {} buffer: {}", lastHeader, in);
    }
    byte headerByte = in.get();
    int headerValue;
    int byteCount = 1;
    if ((headerByte & 0x3f) == 0) {
      // Two byte header
      headerValue = (headerByte & 0xff) << 8 | (in.get() & 0xff);
      byteCount = 2;
    } else if ((headerByte & 0x3f) == 1) {
      // Three byte header
      headerValue = (headerByte & 0xff) << 16 | (in.get() & 0xff) << 8 | (in.get() & 0xff);
      byteCount = 3;
    } else {
      // Single byte header
      headerValue = headerByte & 0xff;
      byteCount = 1;
    }
    final int channelId = RTMPUtils.decodeChannelId(headerValue, byteCount);
    final int headerSize = RTMPUtils.decodeHeaderSize(headerValue, byteCount);
    Header header = new Header();
    header.setChannelId(channelId);
    if (headerSize != HEADER_NEW && lastHeader == null) {
      log.error("Last header null not new, headerSize: {}, channelId {}", headerSize, channelId);
      // this will trigger an error status, which in turn will disconnect the "offending" flash
      // player
      // preventing a memory leak and bringing the whole server to its knees
      return null;
    }
    int timeValue;
    switch (headerSize) {
      case HEADER_NEW:
        // an absolute time value
        timeValue = RTMPUtils.readUnsignedMediumInt(in);
        header.setSize(RTMPUtils.readUnsignedMediumInt(in));
        header.setDataType(in.get());
        header.setStreamId(RTMPUtils.readReverseInt(in));
        if (timeValue == 0xffffff) {
          timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE);
          header.setExtendedTimestamp(timeValue);
        }
        header.setTimerBase(timeValue);
        header.setTimerDelta(0);
        break;
      case HEADER_SAME_SOURCE:
        // a delta time value
        timeValue = RTMPUtils.readUnsignedMediumInt(in);
        header.setSize(RTMPUtils.readUnsignedMediumInt(in));
        header.setDataType(in.get());
        header.setStreamId(lastHeader.getStreamId());
        if (timeValue == 0xffffff) {
          timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE);
          header.setExtendedTimestamp(timeValue);
        } else if (timeValue == 0 && header.getDataType() == TYPE_AUDIO_DATA) {
          // header.setIsGarbage(true);
          log.trace(
              "Audio with zero delta; setting to garbage; ChannelId: {}; DataType: {}; HeaderSize: {}",
              new Object[] {header.getChannelId(), header.getDataType(), headerSize});
        }
        header.setTimerBase(lastHeader.getTimerBase());
        header.setTimerDelta(timeValue);
        break;
      case HEADER_TIMER_CHANGE:
        // a delta time value
        timeValue = RTMPUtils.readUnsignedMediumInt(in);
        header.setSize(lastHeader.getSize());
        header.setDataType(lastHeader.getDataType());
        header.setStreamId(lastHeader.getStreamId());
        if (timeValue == 0xffffff) {
          timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE);
          header.setExtendedTimestamp(timeValue);
        } else if (timeValue == 0 && header.getDataType() == TYPE_AUDIO_DATA) {
          // header.setIsGarbage(true);
          log.trace(
              "Audio with zero delta; setting to garbage; ChannelId: {}; DataType: {}; HeaderSize: {}",
              new Object[] {header.getChannelId(), header.getDataType(), headerSize});
        }
        header.setTimerBase(lastHeader.getTimerBase());
        header.setTimerDelta(timeValue);
        break;
      case HEADER_CONTINUE:
        header.setSize(lastHeader.getSize());
        header.setDataType(lastHeader.getDataType());
        header.setStreamId(lastHeader.getStreamId());
        header.setTimerBase(lastHeader.getTimerBase());
        header.setTimerDelta(lastHeader.getTimerDelta());
        if (lastHeader.getExtendedTimestamp() != 0) {
          timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE);
          header.setExtendedTimestamp(timeValue);
          log.trace("HEADER_CONTINUE with extended timestamp: {}", timeValue);
        }
        break;
      default:
        log.error("Unexpected header size: {}", headerSize);
        return null;
    }

    log.trace("CHUNK, D, {}, {}", header, headerSize);
    return header;
  }
Example #2
0
  public void process() throws IOException {

    while (true) {
      IoBuffer totalBuffer = IoBuffer.allocate(0);
      totalBuffer.setAutoExpand(true);

      byte headerByte = get();
      totalBuffer.put(headerByte);

      int headerValue;
      int byteCount;
      if ((headerByte & 0x3f) == 0) {
        byte b1 = get();
        totalBuffer.put(b1);

        headerValue = (headerByte & 0xff) << 8 | (b1 & 0xff);
        byteCount = 2;
      } else if ((headerByte & 0x3f) == 1) {
        byte b1 = get();
        byte b2 = get();
        totalBuffer.put(b1);
        totalBuffer.put(b2);

        headerValue = (headerByte & 0xff) << 16 | (b1 & 0xff) << 8 | (b2 & 0xff);
        byteCount = 3;
      } else {

        // Single byte header
        headerValue = headerByte & 0xff;
        byteCount = 1;
      }

      final int channelId = RTMPUtils.decodeChannelId(headerValue, byteCount);

      if (channelId < 0) {
        throw new RuntimeException("Bad channel id: " + channelId);
      }

      // Get the header size and length
      byte headerSize = RTMPUtils.decodeHeaderSize(headerValue, byteCount);
      int headerLength = RTMPUtils.getHeaderLength(headerSize);

      Header lastHeader = lastHeaders.get(channelId);
      headerLength += byteCount - 1;

      switch (headerSize) {
        case Constants.HEADER_NEW:
        case Constants.HEADER_SAME_SOURCE:
        case Constants.HEADER_TIMER_CHANGE:
          IoBuffer tempIo = IoBuffer.wrap(get(3));

          int timeValue = RTMPUtils.readUnsignedMediumInt(tempIo);
          if (timeValue == 0xffffff) {
            headerLength += 4;
          }

          totalBuffer.put(tempIo.array());

          break;
        case Constants.HEADER_CONTINUE:
          if (lastHeader != null && lastHeader.getExtendedTimestamp() != 0) {
            headerLength += 4;
          }
          break;
        default:
          throw new RuntimeException("Unexpected header size " + headerSize + " check for error");
      }

      if (totalBuffer.limit() > 1) totalBuffer.put(get(headerLength - totalBuffer.limit()));

      IoBuffer headerBuf = IoBuffer.wrap(totalBuffer.array(), 0, totalBuffer.limit());

      final Header header = decodeHeader(headerBuf, lastHeader);
      if (header == null) {
        throw new RuntimeException("Header is null, check for error");
      }

      lastHeaders.put(channelId, header);

      // check to see if this is a new packets or continue decoding an existing one
      GeneratedMessage packet = lastPackets.get(channelId);
      if (packet == null) {
        packet = new GeneratedMessage(header.clone());
        lastPackets.put(channelId, packet);
      }

      final IoBuffer buf = packet.getData();

      final int readRemaining = header.getSize() - buf.position();
      final int chunkSize = connection.getChunkSize().getSize();
      final int readAmount = (readRemaining > chunkSize) ? chunkSize : readRemaining;

      BufferUtils.put(buf, IoBuffer.wrap(get(readAmount)), readAmount);

      if (buf.position() < header.getSize()) {
        // state.continueDecoding();
        continue;
      }

      if (buf.position() > header.getSize()) {
        System.out.println("Packet size expanded from {} to {} ({})");
      }

      final IoBuffer rawPacketData = createRawPacketData(4096, header.clone(), buf, channelId);

      executor2.execute(
          new Runnable() {

            @Override
            public void run() {
              for (MessageRawListener l : rawListeners) {
                l.onMessage(rawPacketData, header.getDataType());
              }
            }
          });

      /// System.out.println("������������!");
      // System.out.println("CHANNEL: " + channelId);
      // System.out.println("DATA TYPE: " + header.getDataType());
      lastPackets.put(channelId, null);

      try {
        final IRTMPEvent message = decodeMessage(packet.getHeader(), buf);
        // message.setHeader(packet.getHeader());
        // Unfortunately flash will, especially when resetting a video stream with a new key frame,
        // sometime
        // send an earlier time stamp.  To avoid dropping it, we just give it the minimal increment
        // since the
        // last message.  But to avoid relative time stamps being mis-computed, we don't reset the
        // header we stored.
        // final Header lastReadHeader = lastHeaders.get(channelId);

        // lastHeaders.put(channelId, packet.getHeader());
        // packet.setMessage(message);

        // collapse the time stamps on the last packet so that it works right for chunk type 3 later
        lastHeader = lastHeaders.get(channelId);
        lastHeader.setTimerBase(header.getTimer());

        if (message != null) {
          switch (header.getDataType()) {
            case TYPE_PING:
              Ping ping = (Ping) message;

              // Ping p = new Ping(Ping.PING_CLIENT);
              // Header hd = new Header();
              // hd.setDataType((byte)TYPE_PING);
              // writer.write(encoder.encodeEvent(hd, p).array());

              System.out.println("PING TYPE: " + ping.getEventType());
              if (ping.getEventType() == Ping.PING_CLIENT) {
                try {
                  Ping pong = new Ping(Ping.PONG_SERVER);
                  pong.setTimestamp((int) (System.currentTimeMillis() & 0xffffffff));

                  writer.write(encoder.encodeEvent(header, pong).array());
                } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
                }
              }

              break;

            case TYPE_CHUNK_SIZE:
              ChunkSize chunkSIZE = (ChunkSize) message;

              if (connection.isAutoMutableChunkSize()) {
                connection.setChunkSize(chunkSIZE);
              }
              break;
          }
        }

        // System.out.println("HEADER SIZE: " + header.getSize());
        // System.out.println("DATA TIME: " + header.getDataType());
        // System.out.println("CHANNEL ID: " + header.getChannelId());
        // System.out.println("TIMESTAMP: " + header.getTimer());
      } finally {
        lastPackets.put(channelId, null);
      }
    }
  }