Commit 010a00e4 by claincly Committed by Ian Baker

Add support for static RTP payload types.

Some RTP foramts are statically assigned, so they don't have the rtpmap
attribute. Create the missing rtpmap attribute in this case.

PiperOrigin-RevId: 448239724
parent dd365cbe
......@@ -20,7 +20,6 @@ import static com.google.android.exoplayer2.source.rtsp.RtspMessageUtil.parseInt
import static com.google.android.exoplayer2.source.rtsp.SessionDescription.ATTR_FMTP;
import static com.google.android.exoplayer2.source.rtsp.SessionDescription.ATTR_RTPMAP;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import androidx.annotation.Nullable;
......@@ -103,6 +102,17 @@ import java.util.HashMap;
/** Builder class for {@link MediaDescription}. */
public static final class Builder {
/**
* RTPMAP attribute format: {@code <payloadType> <mediaEncoding>/<clockRate>/<channelCount>}.
*/
private static final String RTP_MAP_ATTR_AUDIO_FMT = "%d %s/%d/%d";
private static final int RTP_STATIC_PAYLOAD_TYPE_PCMU = 0;
private static final int RTP_STATIC_PAYLOAD_TYPE_PCMA = 8;
private static final int RTP_STATIC_PAYLOAD_TYPE_L16_STEREO = 10;
private static final int RTP_STATIC_PAYLOAD_TYPE_L16_MONO = 11;
private final String mediaType;
private final int port;
private final String transportProtocol;
......@@ -197,15 +207,55 @@ import java.util.HashMap;
*/
public MediaDescription build() {
try {
// rtpmap attribute is mandatory in RTSP (RFC2326 Section C.1.3).
checkState(attributes.containsKey(ATTR_RTPMAP));
RtpMapAttribute rtpMapAttribute =
RtpMapAttribute.parse(castNonNull(attributes.get(ATTR_RTPMAP)));
attributes.containsKey(ATTR_RTPMAP)
? RtpMapAttribute.parse(castNonNull(attributes.get(ATTR_RTPMAP)))
: RtpMapAttribute.parse(getRtpMapStringByPayloadType(payloadType));
return new MediaDescription(this, ImmutableMap.copyOf(attributes), rtpMapAttribute);
} catch (ParserException e) {
throw new IllegalStateException(e);
}
}
private static String getRtpMapStringByPayloadType(int rtpPayloadType) {
checkArgument(rtpPayloadType < 96);
switch (rtpPayloadType) {
// See RFC3551 Section 6.
case RTP_STATIC_PAYLOAD_TYPE_PCMU:
return constructAudioRtpMap(
RTP_STATIC_PAYLOAD_TYPE_PCMU,
/* mediaEncoding= */ "PCMU",
/* clockRate= */ 8_000,
/* channelCount= */ 1);
case RTP_STATIC_PAYLOAD_TYPE_PCMA:
return constructAudioRtpMap(
RTP_STATIC_PAYLOAD_TYPE_PCMA,
/* mediaEncoding= */ "PCMA",
/* clockRate= */ 8_000,
/* channelCount= */ 1);
case RTP_STATIC_PAYLOAD_TYPE_L16_STEREO:
return constructAudioRtpMap(
RTP_STATIC_PAYLOAD_TYPE_L16_STEREO,
/* mediaEncoding= */ "L16",
/* clockRate= */ 44_100,
/* channelCount= */ 2);
case RTP_STATIC_PAYLOAD_TYPE_L16_MONO:
return constructAudioRtpMap(
RTP_STATIC_PAYLOAD_TYPE_L16_MONO,
/* mediaEncoding= */ "L16",
/* clockRate= */ 44_100,
/* channelCount= */ 1);
default:
throw new IllegalStateException("Unsupported static paylod type " + rtpPayloadType);
}
}
private static String constructAudioRtpMap(
int payloadType, String mediaEncoding, int clockRate, int channelCount) {
return Util.formatInvariant(
RTP_MAP_ATTR_AUDIO_FMT, payloadType, mediaEncoding, clockRate, channelCount);
}
}
/** The media types allowed in a SDP media description. */
......
......@@ -26,6 +26,7 @@ import static org.junit.Assert.assertThrows;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AacUtil;
import com.google.android.exoplayer2.util.MimeTypes;
......@@ -81,6 +82,132 @@ public class RtspMediaTrackTest {
}
@Test
public void generatePayloadFormat_withPcmuMediaDescription_succeeds() {
MediaDescription mediaDescription =
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 0)
.setConnection("IN IP4 0.0.0.0")
.addAttribute(ATTR_CONTROL, "track2")
.build();
RtpPayloadFormat format = RtspMediaTrack.generatePayloadFormat(mediaDescription);
RtpPayloadFormat expectedFormat =
new RtpPayloadFormat(
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_MLAW)
.setChannelCount(1)
.setSampleRate(8_000)
.build(),
/* rtpPayloadType= */ 0,
/* clockRate= */ 8_000,
/* fmtpParameters= */ ImmutableMap.of());
assertThat(format).isEqualTo(expectedFormat);
}
@Test
public void generatePayloadFormat_withPcmaMediaDescription_succeeds() {
// m=audio 0 RTP/AVP 0
// c=IN IP4 0.0.0.0
// a=control:track2
int pcmaPayloadType = 8;
int pcmaClockRate = 8_000;
MediaDescription mediaDescription =
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO,
/* port= */ 0,
RTP_AVP_PROFILE,
/* payloadType= */ pcmaPayloadType)
.setConnection("IN IP4 0.0.0.0")
.addAttribute(ATTR_CONTROL, "track2")
.build();
RtpPayloadFormat format = RtspMediaTrack.generatePayloadFormat(mediaDescription);
RtpPayloadFormat expectedFormat =
new RtpPayloadFormat(
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_ALAW)
.setChannelCount(1)
.setSampleRate(pcmaClockRate)
.build(),
/* rtpPayloadType= */ pcmaPayloadType,
/* clockRate= */ pcmaClockRate,
/* fmtpParameters= */ ImmutableMap.of());
assertThat(format).isEqualTo(expectedFormat);
}
@Test
public void generatePayloadFormat_withL16StereoMediaDescription_succeeds() {
// m=audio 0 RTP/AVP 0
// c=IN IP4 0.0.0.0
// a=control:track2
int l16StereoPayloadType = 10;
int l16StereoClockRate = 44_100;
MediaDescription mediaDescription =
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO,
/* port= */ 0,
RTP_AVP_PROFILE,
/* payloadType= */ l16StereoPayloadType)
.setConnection("IN IP4 0.0.0.0")
.addAttribute(ATTR_CONTROL, "track2")
.build();
RtpPayloadFormat format = RtspMediaTrack.generatePayloadFormat(mediaDescription);
RtpPayloadFormat expectedFormat =
new RtpPayloadFormat(
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_RAW)
.setChannelCount(2)
.setSampleRate(l16StereoClockRate)
.setPcmEncoding(C.ENCODING_PCM_16BIT_BIG_ENDIAN)
.build(),
/* rtpPayloadType= */ l16StereoPayloadType,
/* clockRate= */ l16StereoClockRate,
/* fmtpParameters= */ ImmutableMap.of());
assertThat(format).isEqualTo(expectedFormat);
}
@Test
public void generatePayloadFormat_withL16MonoMediaDescription_succeeds() {
// m=audio 0 RTP/AVP 0
// c=IN IP4 0.0.0.0
// a=control:track2
int l16MonoPayloadType = 11;
int l16MonoClockRate = 44_100;
MediaDescription mediaDescription =
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO,
/* port= */ 0,
RTP_AVP_PROFILE,
/* payloadType= */ l16MonoPayloadType)
.setConnection("IN IP4 0.0.0.0")
.addAttribute(ATTR_CONTROL, "track2")
.build();
RtpPayloadFormat format = RtspMediaTrack.generatePayloadFormat(mediaDescription);
RtpPayloadFormat expectedFormat =
new RtpPayloadFormat(
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_RAW)
.setChannelCount(1)
.setSampleRate(l16MonoClockRate)
.setPcmEncoding(C.ENCODING_PCM_16BIT_BIG_ENDIAN)
.build(),
/* rtpPayloadType= */ l16MonoPayloadType,
/* clockRate= */ l16MonoClockRate,
/* fmtpParameters= */ ImmutableMap.of());
assertThat(format).isEqualTo(expectedFormat);
}
@Test
public void generatePayloadFormat_withFmtpTrailingSemicolon_succeeds() {
MediaDescription mediaDescription =
new MediaDescription.Builder(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment