Commit c15acdf0 by claincly Committed by Oliver Woodman

Allow reading RTSP message body by Content-Length.

Related to Issue: #8941.

RTSP message body's format is not regulated by the RTSP spec, meaning it can
use either CRLF or LF as its line terminator. The old code assumes every line
ends with CRLF (RTSP message and the message body); the new code will rely on
the Content-Length information to receive the bytes for the message body.

#minor-release

PiperOrigin-RevId: 377475565
parent 4d3b98c2
...@@ -38,6 +38,7 @@ import androidx.annotation.Nullable; ...@@ -38,6 +38,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.base.Ascii;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableListMultimap;
...@@ -107,6 +108,8 @@ import java.util.regex.Pattern; ...@@ -107,6 +108,8 @@ import java.util.regex.Pattern;
Pattern.compile("Basic realm=\"([\\w\\s@.]+)\""); Pattern.compile("Basic realm=\"([\\w\\s@.]+)\"");
private static final String RTSP_VERSION = "RTSP/1.0"; private static final String RTSP_VERSION = "RTSP/1.0";
private static final String LF = new String(new byte[] {Ascii.LF});
private static final String CRLF = new String(new byte[] {Ascii.CR, Ascii.LF});
/** /**
* Serializes an {@link RtspRequest} to an {@link ImmutableList} of strings. * Serializes an {@link RtspRequest} to an {@link ImmutableList} of strings.
...@@ -167,7 +170,7 @@ import java.util.regex.Pattern; ...@@ -167,7 +170,7 @@ import java.util.regex.Pattern;
* removed. * removed.
*/ */
public static byte[] convertMessageToByteArray(List<String> message) { public static byte[] convertMessageToByteArray(List<String> message) {
return Joiner.on("\r\n").join(message).getBytes(RtspMessageChannel.CHARSET); return Joiner.on(CRLF).join(message).getBytes(RtspMessageChannel.CHARSET);
} }
/** Removes the user info from the supplied {@link Uri}. */ /** Removes the user info from the supplied {@link Uri}. */
...@@ -211,7 +214,7 @@ import java.util.regex.Pattern; ...@@ -211,7 +214,7 @@ import java.util.regex.Pattern;
/** Returns the corresponding String representation of the {@link RtspRequest.Method} argument. */ /** Returns the corresponding String representation of the {@link RtspRequest.Method} argument. */
public static String toMethodString(@RtspRequest.Method int method) { public static String toMethodString(@RtspRequest.Method int method) {
switch (method) { switch (method) {
case RtspRequest.METHOD_ANNOUNCE: case METHOD_ANNOUNCE:
return "ANNOUNCE"; return "ANNOUNCE";
case METHOD_DESCRIBE: case METHOD_DESCRIBE:
return "DESCRIBE"; return "DESCRIBE";
...@@ -291,7 +294,7 @@ import java.util.regex.Pattern; ...@@ -291,7 +294,7 @@ import java.util.regex.Pattern;
List<String> headerLines = lines.subList(1, messageBodyOffset); List<String> headerLines = lines.subList(1, messageBodyOffset);
RtspHeaders headers = new RtspHeaders.Builder().addAll(headerLines).build(); RtspHeaders headers = new RtspHeaders.Builder().addAll(headerLines).build();
String messageBody = Joiner.on("\r\n").join(lines.subList(messageBodyOffset + 1, lines.size())); String messageBody = Joiner.on(CRLF).join(lines.subList(messageBodyOffset + 1, lines.size()));
return new RtspResponse(statusCode, headers, messageBody); return new RtspResponse(statusCode, headers, messageBody);
} }
...@@ -314,7 +317,7 @@ import java.util.regex.Pattern; ...@@ -314,7 +317,7 @@ import java.util.regex.Pattern;
List<String> headerLines = lines.subList(1, messageBodyOffset); List<String> headerLines = lines.subList(1, messageBodyOffset);
RtspHeaders headers = new RtspHeaders.Builder().addAll(headerLines).build(); RtspHeaders headers = new RtspHeaders.Builder().addAll(headerLines).build();
String messageBody = Joiner.on("\r\n").join(lines.subList(messageBodyOffset + 1, lines.size())); String messageBody = Joiner.on(CRLF).join(lines.subList(messageBodyOffset + 1, lines.size()));
return new RtspRequest(requestUri, method, headers, messageBody); return new RtspRequest(requestUri, method, headers, messageBody);
} }
...@@ -324,6 +327,11 @@ import java.util.regex.Pattern; ...@@ -324,6 +327,11 @@ import java.util.regex.Pattern;
|| STATUS_LINE_PATTERN.matcher(line).matches(); || STATUS_LINE_PATTERN.matcher(line).matches();
} }
/** Returns the lines in an RTSP message body split by the line terminator used in body. */
public static String[] splitRtspMessageBody(String body) {
return Util.split(body, body.contains(CRLF) ? CRLF : LF);
}
/** /**
* Returns the length in bytes if the line contains a Content-Length header, otherwise {@link * Returns the length in bytes if the line contains a Content-Length header, otherwise {@link
* C#LENGTH_UNSET}. * C#LENGTH_UNSET}.
......
...@@ -41,8 +41,6 @@ import java.util.regex.Pattern; ...@@ -41,8 +41,6 @@ import java.util.regex.Pattern;
private static final Pattern MEDIA_DESCRIPTION_PATTERN = private static final Pattern MEDIA_DESCRIPTION_PATTERN =
Pattern.compile("(\\S+)\\s(\\S+)\\s(\\S+)\\s(\\S+)"); Pattern.compile("(\\S+)\\s(\\S+)\\s(\\S+)\\s(\\S+)");
private static final String CRLF = "\r\n";
private static final String VERSION_TYPE = "v"; private static final String VERSION_TYPE = "v";
private static final String ORIGIN_TYPE = "o"; private static final String ORIGIN_TYPE = "o";
private static final String SESSION_TYPE = "s"; private static final String SESSION_TYPE = "s";
...@@ -71,7 +69,7 @@ import java.util.regex.Pattern; ...@@ -71,7 +69,7 @@ import java.util.regex.Pattern;
@Nullable MediaDescription.Builder mediaDescriptionBuilder = null; @Nullable MediaDescription.Builder mediaDescriptionBuilder = null;
// Lines are separated by an CRLF. // Lines are separated by an CRLF.
for (String line : Util.split(sdpString, CRLF)) { for (String line : RtspMessageUtil.splitRtspMessageBody(sdpString)) {
if ("".equals(line)) { if ("".equals(line)) {
continue; continue;
} }
......
...@@ -66,11 +66,21 @@ public final class RtspMessageChannelTest { ...@@ -66,11 +66,21 @@ public final class RtspMessageChannelTest {
.build(), .build(),
"v=安卓アンドロイド\r\n"); "v=安卓アンドロイド\r\n");
RtspResponse describeResponse2 =
new RtspResponse(
200,
new RtspHeaders.Builder()
.add(RtspHeaders.CSEQ, "4")
.add(RtspHeaders.CONTENT_TYPE, "application/sdp")
.add(RtspHeaders.CONTENT_LENGTH, "73")
.build(),
"v=安卓アンドロイド\n" + "o=test 2890844526 2890842807 IN IP4 127.0.0.1\n");
RtspResponse setupResponse = RtspResponse setupResponse =
new RtspResponse( new RtspResponse(
200, 200,
new RtspHeaders.Builder() new RtspHeaders.Builder()
.add(RtspHeaders.CSEQ, "3") .add(RtspHeaders.CSEQ, "5")
.add(RtspHeaders.TRANSPORT, "RTP/AVP/TCP;unicast;interleaved=0-1") .add(RtspHeaders.TRANSPORT, "RTP/AVP/TCP;unicast;interleaved=0-1")
.build(), .build(),
""); "");
...@@ -97,6 +107,8 @@ public final class RtspMessageChannelTest { ...@@ -97,6 +107,8 @@ public final class RtspMessageChannelTest {
convertMessageToByteArray(serializeResponse(optionsResponse))); convertMessageToByteArray(serializeResponse(optionsResponse)));
serverOutputStream.write( serverOutputStream.write(
convertMessageToByteArray(serializeResponse(describeResponse))); convertMessageToByteArray(serializeResponse(describeResponse)));
serverOutputStream.write(
convertMessageToByteArray(serializeResponse(describeResponse2)));
serverOutputStream.write(Bytes.concat(new byte[] {'$'}, interleavedData1)); serverOutputStream.write(Bytes.concat(new byte[] {'$'}, interleavedData1));
serverOutputStream.write(Bytes.concat(new byte[] {'$'}, interleavedData2)); serverOutputStream.write(Bytes.concat(new byte[] {'$'}, interleavedData2));
serverOutputStream.write( serverOutputStream.write(
...@@ -118,7 +130,7 @@ public final class RtspMessageChannelTest { ...@@ -118,7 +130,7 @@ public final class RtspMessageChannelTest {
new RtspMessageChannel( new RtspMessageChannel(
message -> { message -> {
receivedRtspResponses.add(message); receivedRtspResponses.add(message);
if (receivedRtspResponses.size() == 3 && receivedInterleavedData.size() == 2) { if (receivedRtspResponses.size() == 4 && receivedInterleavedData.size() == 2) {
receivingFinished.set(true); receivingFinished.set(true);
} }
}); });
...@@ -148,9 +160,17 @@ public final class RtspMessageChannelTest { ...@@ -148,9 +160,17 @@ public final class RtspMessageChannelTest {
"content-length: 28", "content-length: 28",
"", "",
"v=安卓アンドロイド"), "v=安卓アンドロイド"),
/* describeResponse2 */
ImmutableList.of(
"RTSP/1.0 200 OK",
"cseq: 4",
"content-type: application/sdp",
"content-length: 73",
"",
"v=安卓アンドロイド\n" + "o=test 2890844526 2890842807 IN IP4 127.0.0.1"),
/* setupResponse */ /* setupResponse */
ImmutableList.of( ImmutableList.of(
"RTSP/1.0 200 OK", "cseq: 3", "transport: RTP/AVP/TCP;unicast;interleaved=0-1", "")) "RTSP/1.0 200 OK", "cseq: 5", "transport: RTP/AVP/TCP;unicast;interleaved=0-1", ""))
.inOrder(); .inOrder();
assertThat(receivedInterleavedData) assertThat(receivedInterleavedData)
.containsExactly( .containsExactly(
......
...@@ -480,4 +480,18 @@ public final class RtspMessageUtilTest { ...@@ -480,4 +480,18 @@ public final class RtspMessageUtilTest {
assertThat(authenticationInfo.realm).isEqualTo("LIVE555 Streaming Media"); assertThat(authenticationInfo.realm).isEqualTo("LIVE555 Streaming Media");
assertThat(authenticationInfo.opaque).isEmpty(); assertThat(authenticationInfo.opaque).isEmpty();
} }
@Test
public void splitRtspMessageBody_withCrLfLineTerminatorMessageBody_splitsMessageBody() {
String[] lines = RtspMessageUtil.splitRtspMessageBody("line1\r\nline2\r\nline3");
assertThat(lines).asList().containsExactly("line1", "line2", "line3").inOrder();
}
@Test
public void splitRtspMessageBody_withLfLineTerminatorMessageBody_splitsMessageBody() {
String[] lines = RtspMessageUtil.splitRtspMessageBody("line1\nline2\nline3");
assertThat(lines).asList().containsExactly("line1", "line2", "line3").inOrder();
}
} }
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