public final class

RtpPacket

extends java.lang.Object

 java.lang.Object

↳androidx.media3.exoplayer.rtsp.RtpPacket

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer-rtsp', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer-rtsp
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-exoplayer-rtsp:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Represents the header and the payload of an RTP packet.

Not supported parsing at the moment: header extension and CSRC.

Structure of an RTP header (RFC3550, Section 5.1).

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |V=2|P|X|  CC   |M|     PT      |       sequence number         |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                           timestamp                           |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |           synchronization source (SSRC) identifier            |
 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 |            contributing source (CSRC) identifiers             |
 |                             ....                              |
 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 | Profile-specific extension ID |   Extension header length     |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                       Extension header                        |
 |                             ....                              |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    3                   2                   1
  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
 

Summary

Fields
public final byte[]csrc

The RTP CSRC fields (Optional, up to 15 items).

public static final intCSRC_SIZE

public final bytecsrcCount

The RTP CSRC count field (Word 0, bits 4-7).

public final booleanextension

The RTP extension bit (Word 0, bit 3).

public final booleanmarker

The RTP marker bit (Word 0, bit 8).

public static final intMAX_SEQUENCE_NUMBER

public static final intMAX_SIZE

public static final intMIN_HEADER_SIZE

public static final intMIN_SEQUENCE_NUMBER

public final booleanpadding

The RTP padding bit (Word 0, bit 2).

public final byte[]payloadData

public final bytepayloadType

The RTP CSRC count field (Word 0, bits 9-15).

public static final intRTP_VERSION

public final intsequenceNumber

The RTP sequence number field (Word 0, bits 16-31).

public final intssrc

The RTP SSRC field (Word 2).

public final longtimestamp

The RTP timestamp field (Word 1).

public final byteversion

The RTP version field (Word 0, bits 0-1), should always be 2.

Methods
public booleanequals(java.lang.Object o)

public static intgetNextSequenceNumber(int sequenceNumber)

Returns the next sequence number of the sequenceNumber.

public static intgetPreviousSequenceNumber(int sequenceNumber)

Returns the previous sequence number from the sequenceNumber.

public inthashCode()

public static RtpPacketparse(byte[] buffer[], int length)

Creates an RtpPacket from a byte array.

public static RtpPacketparse(ParsableByteArray packetBuffer)

Creates an RtpPacket from a ParsableByteArray.

public java.lang.StringtoString()

public intwriteToBuffer(byte[] target[], int offset, int length)

Writes the data in an RTP packet to a target buffer.

from java.lang.Objectclone, finalize, getClass, notify, notifyAll, wait, wait, wait

Fields

public static final int RTP_VERSION

public static final int MAX_SIZE

public static final int MIN_HEADER_SIZE

public static final int MIN_SEQUENCE_NUMBER

public static final int MAX_SEQUENCE_NUMBER

public static final int CSRC_SIZE

public final byte version

The RTP version field (Word 0, bits 0-1), should always be 2.

public final boolean padding

The RTP padding bit (Word 0, bit 2).

public final boolean extension

The RTP extension bit (Word 0, bit 3).

public final byte csrcCount

The RTP CSRC count field (Word 0, bits 4-7).

public final boolean marker

The RTP marker bit (Word 0, bit 8).

public final byte payloadType

The RTP CSRC count field (Word 0, bits 9-15).

public final int sequenceNumber

The RTP sequence number field (Word 0, bits 16-31).

public final long timestamp

The RTP timestamp field (Word 1).

public final int ssrc

The RTP SSRC field (Word 2).

public final byte[] csrc

The RTP CSRC fields (Optional, up to 15 items).

public final byte[] payloadData

Methods

public static int getNextSequenceNumber(int sequenceNumber)

Returns the next sequence number of the sequenceNumber.

public static int getPreviousSequenceNumber(int sequenceNumber)

Returns the previous sequence number from the sequenceNumber.

public static RtpPacket parse(ParsableByteArray packetBuffer)

Creates an RtpPacket from a ParsableByteArray.

Parameters:

packetBuffer: The buffer that contains the RTP packet data.

Returns:

The built RtpPacket.

public static RtpPacket parse(byte[] buffer[], int length)

Creates an RtpPacket from a byte array.

Parameters:

buffer: The buffer that contains the RTP packet data.
length: The length of the RTP packet.

Returns:

The built RtpPacket.

public int writeToBuffer(byte[] target[], int offset, int length)

Writes the data in an RTP packet to a target buffer.

The size of the target buffer and the length argument should be big enough so that the entire RTP packet could fit. That is, if there is not enough space to store the entire RTP packet, no bytes will be written. The maximum size of an RTP packet is defined as RtpPacket.MAX_SIZE.

Parameters:

target: A target byte buffer to which the packet data is copied.
offset: The offset into the target array at which to write.
length: The maximum number of bytes that can be written.

Returns:

The number of bytes written, or C.LENGTH_UNSET if there is not enough space to write the packet.

public boolean equals(java.lang.Object o)

public int hashCode()

public java.lang.String toString()

Source

/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.media3.exoplayer.rtsp;

import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;

import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.math.IntMath;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.nio.ByteBuffer;

/**
 * Represents the header and the payload of an RTP packet.
 *
 * <p>Not supported parsing at the moment: header extension and CSRC.
 *
 * <p>Structure of an RTP header (RFC3550, Section 5.1).
 *
 * <pre>
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |V=2|P|X|  CC   |M|     PT      |       sequence number         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                           timestamp                           |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |           synchronization source (SSRC) identifier            |
 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 * |            contributing source (CSRC) identifiers             |
 * |                             ....                              |
 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 * | Profile-specific extension ID |   Extension header length     |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                       Extension header                        |
 * |                             ....                              |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *    3                   2                   1
 *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
 * </pre>
 */
@UnstableApi
public final class RtpPacket {

  /** Builder class for an {@link RtpPacket} */
  public static final class Builder {
    private boolean padding;
    private boolean marker;
    private byte payloadType;
    private int sequenceNumber;
    private long timestamp;
    private int ssrc;
    private byte[] csrc = EMPTY;
    private byte[] payloadData = EMPTY;

    /** Sets the {@link RtpPacket#padding}. The default is false. */
    @CanIgnoreReturnValue
    public Builder setPadding(boolean padding) {
      this.padding = padding;
      return this;
    }

    /** Sets {@link RtpPacket#marker}. The default is false. */
    @CanIgnoreReturnValue
    public Builder setMarker(boolean marker) {
      this.marker = marker;
      return this;
    }

    /** Sets {@link RtpPacket#payloadType}. The default is 0. */
    @CanIgnoreReturnValue
    public Builder setPayloadType(byte payloadType) {
      this.payloadType = payloadType;
      return this;
    }

    /** Sets {@link RtpPacket#sequenceNumber}. The default is 0. */
    @CanIgnoreReturnValue
    public Builder setSequenceNumber(int sequenceNumber) {
      checkArgument(sequenceNumber >= MIN_SEQUENCE_NUMBER && sequenceNumber <= MAX_SEQUENCE_NUMBER);
      this.sequenceNumber = sequenceNumber & 0xFFFF;
      return this;
    }

    /** Sets {@link RtpPacket#timestamp}. The default is 0. */
    @CanIgnoreReturnValue
    public Builder setTimestamp(long timestamp) {
      this.timestamp = timestamp;
      return this;
    }

    /** Sets {@link RtpPacket#ssrc}. The default is 0. */
    @CanIgnoreReturnValue
    public Builder setSsrc(int ssrc) {
      this.ssrc = ssrc;
      return this;
    }

    /** Sets {@link RtpPacket#csrc}. The default is an empty byte array. */
    @CanIgnoreReturnValue
    public Builder setCsrc(byte[] csrc) {
      checkNotNull(csrc);
      this.csrc = csrc;
      return this;
    }

    /** Sets {@link RtpPacket#payloadData}. The default is an empty byte array. */
    @CanIgnoreReturnValue
    public Builder setPayloadData(byte[] payloadData) {
      checkNotNull(payloadData);
      this.payloadData = payloadData;
      return this;
    }

    /** Builds the {@link RtpPacket}. */
    public RtpPacket build() {
      return new RtpPacket(this);
    }
  }

  public static final int RTP_VERSION = 2;

  public static final int MAX_SIZE = 65507;
  public static final int MIN_HEADER_SIZE = 12;
  public static final int MIN_SEQUENCE_NUMBER = 0;
  public static final int MAX_SEQUENCE_NUMBER = 0xFFFF;
  public static final int CSRC_SIZE = 4;

  /** Returns the next sequence number of the {@code sequenceNumber}. */
  public static int getNextSequenceNumber(int sequenceNumber) {
    return IntMath.mod(sequenceNumber + 1, MAX_SEQUENCE_NUMBER + 1);
  }

  /** Returns the previous sequence number from the {@code sequenceNumber}. */
  public static int getPreviousSequenceNumber(int sequenceNumber) {
    return IntMath.mod(sequenceNumber - 1, MAX_SEQUENCE_NUMBER + 1);
  }

  private static final byte[] EMPTY = new byte[0];

  /** The RTP version field (Word 0, bits 0-1), should always be 2. */
  public final byte version = RTP_VERSION;

  /** The RTP padding bit (Word 0, bit 2). */
  public final boolean padding;

  /** The RTP extension bit (Word 0, bit 3). */
  public final boolean extension;

  /** The RTP CSRC count field (Word 0, bits 4-7). */
  public final byte csrcCount;

  /** The RTP marker bit (Word 0, bit 8). */
  public final boolean marker;

  /** The RTP CSRC count field (Word 0, bits 9-15). */
  public final byte payloadType;

  /** The RTP sequence number field (Word 0, bits 16-31). */
  public final int sequenceNumber;

  /** The RTP timestamp field (Word 1). */
  public final long timestamp;

  /** The RTP SSRC field (Word 2). */
  public final int ssrc;

  /** The RTP CSRC fields (Optional, up to 15 items). */
  public final byte[] csrc;

  public final byte[] payloadData;

  /**
   * Creates an {@link RtpPacket} from a {@link ParsableByteArray}.
   *
   * @param packetBuffer The buffer that contains the RTP packet data.
   * @return The built {@link RtpPacket}.
   */
  @Nullable
  public static RtpPacket parse(ParsableByteArray packetBuffer) {
    if (packetBuffer.bytesLeft() < MIN_HEADER_SIZE) {
      return null;
    }

    // Word 0.
    int firstByte = packetBuffer.readUnsignedByte();
    byte version = (byte) (firstByte >> 6);
    boolean padding = ((firstByte >> 5) & 0x1) == 1;
    byte csrcCount = (byte) (firstByte & 0xF);

    if (version != RTP_VERSION) {
      return null;
    }

    int secondByte = packetBuffer.readUnsignedByte();
    boolean marker = ((secondByte >> 7) & 0x1) == 1;
    byte payloadType = (byte) (secondByte & 0x7F);

    int sequenceNumber = packetBuffer.readUnsignedShort();

    // Word 1.
    long timestamp = packetBuffer.readUnsignedInt();

    // Word 2.
    int ssrc = packetBuffer.readInt();

    // CSRC.
    byte[] csrc;
    if (csrcCount > 0) {
      csrc = new byte[csrcCount * CSRC_SIZE];
      for (int i = 0; i < csrcCount; i++) {
        packetBuffer.readBytes(csrc, i * CSRC_SIZE, CSRC_SIZE);
      }
    } else {
      csrc = EMPTY;
    }

    // Everything else will be RTP payload.
    byte[] payloadData = new byte[packetBuffer.bytesLeft()];
    packetBuffer.readBytes(payloadData, 0, packetBuffer.bytesLeft());

    Builder builder = new Builder();
    return builder
        .setPadding(padding)
        .setMarker(marker)
        .setPayloadType(payloadType)
        .setSequenceNumber(sequenceNumber)
        .setTimestamp(timestamp)
        .setSsrc(ssrc)
        .setCsrc(csrc)
        .setPayloadData(payloadData)
        .build();
  }

  /**
   * Creates an {@link RtpPacket} from a byte array.
   *
   * @param buffer The buffer that contains the RTP packet data.
   * @param length The length of the RTP packet.
   * @return The built {@link RtpPacket}.
   */
  @Nullable
  public static RtpPacket parse(byte[] buffer, int length) {
    return parse(new ParsableByteArray(buffer, length));
  }

  private RtpPacket(Builder builder) {
    this.padding = builder.padding;
    this.extension = false;
    this.marker = builder.marker;
    this.payloadType = builder.payloadType;
    this.sequenceNumber = builder.sequenceNumber;
    this.timestamp = builder.timestamp;
    this.ssrc = builder.ssrc;
    this.csrc = builder.csrc;
    this.csrcCount = (byte) (this.csrc.length / CSRC_SIZE);
    this.payloadData = builder.payloadData;
  }

  /**
   * Writes the data in an RTP packet to a target buffer.
   *
   * <p>The size of the target buffer and the length argument should be big enough so that the
   * entire RTP packet could fit. That is, if there is not enough space to store the entire RTP
   * packet, no bytes will be written. The maximum size of an RTP packet is defined as {@link
   * RtpPacket#MAX_SIZE}.
   *
   * @param target A target byte buffer to which the packet data is copied.
   * @param offset The offset into the target array at which to write.
   * @param length The maximum number of bytes that can be written.
   * @return The number of bytes written, or {@link C#LENGTH_UNSET} if there is not enough space to
   *     write the packet.
   */
  public int writeToBuffer(byte[] target, int offset, int length) {
    int packetLength = MIN_HEADER_SIZE + (CSRC_SIZE * csrcCount) + payloadData.length;
    if (length < packetLength || target.length - offset < packetLength) {
      return C.LENGTH_UNSET;
    }

    ByteBuffer buffer = ByteBuffer.wrap(target, offset, length);
    byte firstByte =
        (byte)
            ((version << 6)
                | ((padding ? 1 : 0) << 5)
                | ((extension ? 1 : 0) << 4)
                | (csrcCount & 0xF));
    byte secondByte = (byte) (((marker ? 1 : 0) << 7) | (payloadType & 0x7F));
    buffer
        .put(firstByte)
        .put(secondByte)
        .putShort((short) sequenceNumber)
        .putInt((int) timestamp)
        .putInt(ssrc)
        .put(csrc)
        .put(payloadData);
    return packetLength;
  }

  @Override
  public boolean equals(@Nullable Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    RtpPacket rtpPacket = (RtpPacket) o;
    return payloadType == rtpPacket.payloadType
        && sequenceNumber == rtpPacket.sequenceNumber
        && marker == rtpPacket.marker
        && timestamp == rtpPacket.timestamp
        && ssrc == rtpPacket.ssrc;
  }

  @Override
  public int hashCode() {
    int result = 17;
    result = 31 * result + payloadType;
    result = 31 * result + sequenceNumber;
    result = 31 * result + (marker ? 1 : 0);
    result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
    result = 31 * result + ssrc;
    return result;
  }

  @Override
  public String toString() {
    return Util.formatInvariant(
        "RtpPacket(payloadType=%d, seq=%d, timestamp=%d, ssrc=%x, marker=%b)",
        payloadType, sequenceNumber, timestamp, ssrc, marker);
  }
}