Use RTSP header names as defined in the spec.

Using all lower case RTSP headers is perfectly valid, as the spec mandates case
insensitively. However, some RTSP servers do not implement the RTSP spec
closely and does not recognize the lower case headers. This change aims
at using the header names as defined in the RTSP spec to maximize compatibility.

Issue: #9182
PiperOrigin-RevId: 386224566
This commit is contained in:
claincly 2021-07-22 14:26:58 +01:00 committed by kim-vde
parent e2f1285ada
commit 13ff72d869
5 changed files with 146 additions and 72 deletions

View File

@ -43,8 +43,8 @@
`Player.EVENT_STATIC_METADATA_CHANGED`. Use `Player.getMediaMetadata`,
`Player.Listener.onMediaMetadataChanged` and
`Player.EVENT_MEDIA_METADATA_CHANGED` for convenient access to
structured metadata, or access the raw static metadata directly from
the `TrackSelection#getFormat()`.
structured metadata, or access the raw static metadata directly from the
`TrackSelection#getFormat()`.
* Remove deprecated symbols:
* Remove `Player.getPlaybackError`. Use `Player.getPlayerError` instead.
* Remove `Player.getCurrentTag`. Use `Player.getCurrentMediaItem` and
@ -115,6 +115,9 @@
* Deprecate `setControlDispatcher` in `LeanbackPlayerAdapter`.
* Media2 extension:
* Deprecate `setControlDispatcher` in `SessionPlayerConnector`.
* RTSP:
* Use standard RTSP header names.
([#9182](https://github.com/google/ExoPlayer/issues/9182)).
### 2.14.2 (2021-07-20)

View File

@ -34,37 +34,37 @@ import java.util.Map;
*/
/* package */ final class RtspHeaders {
public static final String ACCEPT = "accept";
public static final String ALLOW = "allow";
public static final String AUTHORIZATION = "authorization";
public static final String BANDWIDTH = "bandwidth";
public static final String BLOCKSIZE = "blocksize";
public static final String CACHE_CONTROL = "cache-control";
public static final String CONNECTION = "connection";
public static final String CONTENT_BASE = "content-base";
public static final String CONTENT_ENCODING = "content-encoding";
public static final String CONTENT_LANGUAGE = "content-language";
public static final String CONTENT_LENGTH = "content-length";
public static final String CONTENT_LOCATION = "content-location";
public static final String CONTENT_TYPE = "content-type";
public static final String CSEQ = "cseq";
public static final String DATE = "date";
public static final String EXPIRES = "expires";
public static final String PROXY_AUTHENTICATE = "proxy-authenticate";
public static final String PROXY_REQUIRE = "proxy-require";
public static final String PUBLIC = "public";
public static final String RANGE = "range";
public static final String RTP_INFO = "rtp-info";
public static final String RTCP_INTERVAL = "rtcp-interval";
public static final String SCALE = "scale";
public static final String SESSION = "session";
public static final String SPEED = "speed";
public static final String SUPPORTED = "supported";
public static final String TIMESTAMP = "timestamp";
public static final String TRANSPORT = "transport";
public static final String USER_AGENT = "user-agent";
public static final String VIA = "via";
public static final String WWW_AUTHENTICATE = "www-authenticate";
public static final String ACCEPT = "Accept";
public static final String ALLOW = "Allow";
public static final String AUTHORIZATION = "Authorization";
public static final String BANDWIDTH = "Bandwidth";
public static final String BLOCKSIZE = "Blocksize";
public static final String CACHE_CONTROL = "Cache-Control";
public static final String CONNECTION = "Connection";
public static final String CONTENT_BASE = "Content-Base";
public static final String CONTENT_ENCODING = "Content-Encoding";
public static final String CONTENT_LANGUAGE = "Content-Language";
public static final String CONTENT_LENGTH = "Content-Length";
public static final String CONTENT_LOCATION = "Content-Location";
public static final String CONTENT_TYPE = "Content-Type";
public static final String CSEQ = "CSeq";
public static final String DATE = "Date";
public static final String EXPIRES = "Expires";
public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
public static final String PROXY_REQUIRE = "Proxy-Require";
public static final String PUBLIC = "Public";
public static final String RANGE = "Range";
public static final String RTP_INFO = "RTP-Info";
public static final String RTCP_INTERVAL = "RTCP-Interval";
public static final String SCALE = "Scale";
public static final String SESSION = "Session";
public static final String SPEED = "Speed";
public static final String SUPPORTED = "Supported";
public static final String TIMESTAMP = "Timestamp";
public static final String TRANSPORT = "Transport";
public static final String USER_AGENT = "User-Agent";
public static final String VIA = "Via";
public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
/** An empty header object. */
public static final RtspHeaders EMPTY = new RtspHeaders.Builder().build();
@ -96,7 +96,7 @@ import java.util.Map;
* @return This builder.
*/
public Builder add(String headerName, String headerValue) {
namesAndValuesBuilder.put(Ascii.toLowerCase(headerName.trim()), headerValue.trim());
namesAndValuesBuilder.put(convertToStandardHeaderName(headerName.trim()), headerValue.trim());
return this;
}
@ -194,10 +194,77 @@ import java.util.Map;
* list is empty if the header name is not recorded.
*/
public ImmutableList<String> values(String headerName) {
return namesAndValues.get(Ascii.toLowerCase(headerName));
return namesAndValues.get(convertToStandardHeaderName(headerName));
}
private RtspHeaders(Builder builder) {
this.namesAndValues = builder.namesAndValuesBuilder.build();
}
private static String convertToStandardHeaderName(String messageHeaderName) {
if (Ascii.equalsIgnoreCase(messageHeaderName, ACCEPT)) {
return ACCEPT;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, ALLOW)) {
return ALLOW;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, AUTHORIZATION)) {
return AUTHORIZATION;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, BANDWIDTH)) {
return BANDWIDTH;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, BLOCKSIZE)) {
return BLOCKSIZE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CACHE_CONTROL)) {
return CACHE_CONTROL;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CONNECTION)) {
return CONNECTION;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CONTENT_BASE)) {
return CONTENT_BASE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CONTENT_ENCODING)) {
return CONTENT_ENCODING;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CONTENT_LANGUAGE)) {
return CONTENT_LANGUAGE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CONTENT_LENGTH)) {
return CONTENT_LENGTH;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CONTENT_LOCATION)) {
return CONTENT_LOCATION;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CONTENT_TYPE)) {
return CONTENT_TYPE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, CSEQ)) {
return CSEQ;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, DATE)) {
return DATE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, EXPIRES)) {
return EXPIRES;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, PROXY_AUTHENTICATE)) {
return PROXY_AUTHENTICATE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, PROXY_REQUIRE)) {
return PROXY_REQUIRE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, PUBLIC)) {
return PUBLIC;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, RANGE)) {
return RANGE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, RTP_INFO)) {
return RTP_INFO;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, RTCP_INTERVAL)) {
return RTCP_INTERVAL;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, SCALE)) {
return SCALE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, SESSION)) {
return SESSION;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, SPEED)) {
return SPEED;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, SUPPORTED)) {
return SUPPORTED;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, TIMESTAMP)) {
return TIMESTAMP;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, TRANSPORT)) {
return TRANSPORT;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, USER_AGENT)) {
return USER_AGENT;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, VIA)) {
return VIA;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, WWW_AUTHENTICATE)) {
return WWW_AUTHENTICATE;
}
return messageHeaderName;
}
}

View File

@ -160,10 +160,10 @@ public final class RtspHeadersTest {
.build();
assertThat(headers.asMultiMap())
.containsExactly(
"accept", "application/sdp",
"cseq", "3",
"content-length", "707",
"transport", "RTP/AVP;unicast;client_port=65458-65459");
"Accept", "application/sdp",
"CSeq", "3",
"Content-Length", "707",
"Transport", "RTP/AVP;unicast;client_port=65458-65459");
}
@Test
@ -182,14 +182,16 @@ public final class RtspHeadersTest {
.build();
ListMultimap<String, String> headersMap = headers.asMultiMap();
assertThat(headersMap.keySet()).containsExactly("accept", "cseq", "transport").inOrder();
assertThat(headersMap.keySet())
.containsExactly(RtspHeaders.ACCEPT, RtspHeaders.CSEQ, RtspHeaders.TRANSPORT)
.inOrder();
assertThat(headersMap)
.valuesForKey("accept")
.valuesForKey(RtspHeaders.ACCEPT)
.containsExactly("application/sdp", "application/sip")
.inOrder();
assertThat(headersMap).valuesForKey("cseq").containsExactly("3", "5").inOrder();
assertThat(headersMap).valuesForKey(RtspHeaders.CSEQ).containsExactly("3", "5").inOrder();
assertThat(headersMap)
.valuesForKey("transport")
.valuesForKey(RtspHeaders.TRANSPORT)
.containsExactly(
"RTP/AVP;unicast;client_port=65456-65457", "RTP/AVP;unicast;client_port=65458-65459")
.inOrder();

View File

@ -149,26 +149,26 @@ public final class RtspMessageChannelTest {
assertThat(receivedRtspResponses)
.containsExactly(
/* optionsResponse */
ImmutableList.of("RTSP/1.0 200 OK", "cseq: 2", "public: OPTIONS", ""),
ImmutableList.of("RTSP/1.0 200 OK", "CSeq: 2", "Public: OPTIONS", ""),
/* describeResponse */
ImmutableList.of(
"RTSP/1.0 200 OK",
"cseq: 3",
"content-type: application/sdp",
"content-length: 28",
"CSeq: 3",
"Content-Type: application/sdp",
"Content-Length: 28",
"",
"v=安卓アンドロイド"),
/* describeResponse2 */
ImmutableList.of(
"RTSP/1.0 200 OK",
"cseq: 4",
"content-type: application/sdp",
"content-length: 73",
"CSeq: 4",
"Content-Type: application/sdp",
"Content-Length: 73",
"",
"v=安卓アンドロイド\n" + "o=test 2890844526 2890842807 IN IP4 127.0.0.1"),
/* setupResponse */
ImmutableList.of(
"RTSP/1.0 200 OK", "cseq: 5", "transport: RTP/AVP/TCP;unicast;interleaved=0-1", ""))
"RTSP/1.0 200 OK", "CSeq: 5", "Transport: RTP/AVP/TCP;unicast;interleaved=0-1", ""))
.inOrder();
assertThat(receivedInterleavedData)
.containsExactly(

View File

@ -147,10 +147,12 @@ public final class RtspMessageUtilTest {
assertThat(response.status).isEqualTo(401);
assertThat(headersMap.keySet()).containsExactly("cseq", "www-authenticate").inOrder();
assertThat(headersMap).valuesForKey("cseq").containsExactly("3");
assertThat(headersMap.keySet())
.containsExactly(RtspHeaders.CSEQ, RtspHeaders.WWW_AUTHENTICATE)
.inOrder();
assertThat(headersMap).valuesForKey(RtspHeaders.CSEQ).containsExactly("3");
assertThat(headersMap)
.valuesForKey("www-authenticate")
.valuesForKey(RtspHeaders.WWW_AUTHENTICATE)
.containsExactly("BASIC realm=\"wow\"", "DIGEST realm=\"wow\", nonce=\"nonce\"")
.inOrder();
@ -221,14 +223,14 @@ public final class RtspMessageUtilTest {
List<String> expectedLines =
Arrays.asList(
"SETUP rtsp://127.0.0.1/test.mkv/track1 RTSP/1.0",
"cseq: 4",
"transport: RTP/AVP;unicast;client_port=65458-65459",
"CSeq: 4",
"Transport: RTP/AVP;unicast;client_port=65458-65459",
"",
"");
String expectedRtspMessage =
"SETUP rtsp://127.0.0.1/test.mkv/track1 RTSP/1.0\r\n"
+ "cseq: 4\r\n"
+ "transport: RTP/AVP;unicast;client_port=65458-65459\r\n"
+ "CSeq: 4\r\n"
+ "Transport: RTP/AVP;unicast;client_port=65458-65459\r\n"
+ "\r\n";
assertThat(messageLines).isEqualTo(expectedLines);
@ -254,14 +256,14 @@ public final class RtspMessageUtilTest {
List<String> expectedLines =
Arrays.asList(
"RTSP/1.0 200 OK",
"cseq: 4",
"transport: RTP/AVP;unicast;client_port=65458-65459;server_port=5354-5355",
"CSeq: 4",
"Transport: RTP/AVP;unicast;client_port=65458-65459;server_port=5354-5355",
"",
"");
String expectedRtspMessage =
"RTSP/1.0 200 OK\r\n"
+ "cseq: 4\r\n"
+ "transport: RTP/AVP;unicast;client_port=65458-65459;server_port=5354-5355\r\n"
+ "CSeq: 4\r\n"
+ "Transport: RTP/AVP;unicast;client_port=65458-65459;server_port=5354-5355\r\n"
+ "\r\n";
assertThat(messageLines).isEqualTo(expectedLines);
assertThat(RtspMessageUtil.convertMessageToByteArray(messageLines))
@ -296,10 +298,10 @@ public final class RtspMessageUtilTest {
List<String> expectedLines =
Arrays.asList(
"RTSP/1.0 200 OK",
"cseq: 4",
"content-base: rtsp://127.0.0.1/test.mkv/",
"content-type: application/sdp",
"content-length: 707",
"CSeq: 4",
"Content-Base: rtsp://127.0.0.1/test.mkv/",
"Content-Type: application/sdp",
"Content-Length: 707",
"",
"v=0\r\n"
+ "o=- 1606776316530225 1 IN IP4 192.168.2.176\r\n"
@ -313,10 +315,10 @@ public final class RtspMessageUtilTest {
String expectedRtspMessage =
"RTSP/1.0 200 OK\r\n"
+ "cseq: 4\r\n"
+ "content-base: rtsp://127.0.0.1/test.mkv/\r\n"
+ "content-type: application/sdp\r\n"
+ "content-length: 707\r\n"
+ "CSeq: 4\r\n"
+ "Content-Base: rtsp://127.0.0.1/test.mkv/\r\n"
+ "Content-Type: application/sdp\r\n"
+ "Content-Length: 707\r\n"
+ "\r\n"
+ "v=0\r\n"
+ "o=- 1606776316530225 1 IN IP4 192.168.2.176\r\n"
@ -340,8 +342,8 @@ public final class RtspMessageUtilTest {
/* status= */ 454, new RtspHeaders.Builder().add(RtspHeaders.CSEQ, "4").build());
List<String> messageLines = RtspMessageUtil.serializeResponse(response);
List<String> expectedLines = Arrays.asList("RTSP/1.0 454 Session Not Found", "cseq: 4", "", "");
String expectedRtspMessage = "RTSP/1.0 454 Session Not Found\r\n" + "cseq: 4\r\n" + "\r\n";
List<String> expectedLines = Arrays.asList("RTSP/1.0 454 Session Not Found", "CSeq: 4", "", "");
String expectedRtspMessage = "RTSP/1.0 454 Session Not Found\r\n" + "CSeq: 4\r\n" + "\r\n";
assertThat(RtspMessageUtil.serializeResponse(response)).isEqualTo(expectedLines);
assertThat(RtspMessageUtil.convertMessageToByteArray(messageLines))