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
This commit is contained in:
parent
dd365cbeb8
commit
010a00e458
@ -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;
|
||||
@ -80,6 +81,132 @@ public class RtspMediaTrackTest {
|
||||
assertThat(format).isEqualTo(expectedFormat);
|
||||
}
|
||||
|
||||
@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 =
|
||||
|
Loading…
x
Reference in New Issue
Block a user