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_FMTP;
|
||||||
import static com.google.android.exoplayer2.source.rtsp.SessionDescription.ATTR_RTPMAP;
|
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.checkArgument;
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
|
||||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -103,6 +102,17 @@ import java.util.HashMap;
|
|||||||
|
|
||||||
/** Builder class for {@link MediaDescription}. */
|
/** Builder class for {@link MediaDescription}. */
|
||||||
public static final class Builder {
|
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 String mediaType;
|
||||||
private final int port;
|
private final int port;
|
||||||
private final String transportProtocol;
|
private final String transportProtocol;
|
||||||
@ -197,15 +207,55 @@ import java.util.HashMap;
|
|||||||
*/
|
*/
|
||||||
public MediaDescription build() {
|
public MediaDescription build() {
|
||||||
try {
|
try {
|
||||||
// rtpmap attribute is mandatory in RTSP (RFC2326 Section C.1.3).
|
|
||||||
checkState(attributes.containsKey(ATTR_RTPMAP));
|
|
||||||
RtpMapAttribute rtpMapAttribute =
|
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);
|
return new MediaDescription(this, ImmutableMap.copyOf(attributes), rtpMapAttribute);
|
||||||
} catch (ParserException e) {
|
} catch (ParserException e) {
|
||||||
throw new IllegalStateException(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. */
|
/** The media types allowed in a SDP media description. */
|
||||||
|
@ -26,6 +26,7 @@ import static org.junit.Assert.assertThrows;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.audio.AacUtil;
|
import com.google.android.exoplayer2.audio.AacUtil;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
@ -80,6 +81,132 @@ public class RtspMediaTrackTest {
|
|||||||
assertThat(format).isEqualTo(expectedFormat);
|
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
|
@Test
|
||||||
public void generatePayloadFormat_withFmtpTrailingSemicolon_succeeds() {
|
public void generatePayloadFormat_withFmtpTrailingSemicolon_succeeds() {
|
||||||
MediaDescription mediaDescription =
|
MediaDescription mediaDescription =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user