mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Handle malformed URL in RTP-Info header.
Some server will send partial URIs in the RTP-Info header, while the RTSP spec requires absolute URLs. Issue: #9346 #exofixit PiperOrigin-RevId: 395452741
This commit is contained in:
parent
c403de1c19
commit
e6b5392e63
@ -78,6 +78,8 @@
|
|||||||
* RTSP:
|
* RTSP:
|
||||||
* Handle when additional spaces are in SDP's RTPMAP atrribute
|
* Handle when additional spaces are in SDP's RTPMAP atrribute
|
||||||
([#9379](https://github.com/google/ExoPlayer/issues/9379)).
|
([#9379](https://github.com/google/ExoPlayer/issues/9379)).
|
||||||
|
* Handle partial URIs in RTP-Info headers
|
||||||
|
([#9346](https://github.com/google/ExoPlayer/issues/9346)).
|
||||||
* Extractors:
|
* Extractors:
|
||||||
* ID3: Fix issue decoding ID3 tags containing UTF-16 encoded strings
|
* ID3: Fix issue decoding ID3 tags containing UTF-16 encoded strings
|
||||||
([#9087](https://github.com/google/ExoPlayer/issues/9087)).
|
([#9087](https://github.com/google/ExoPlayer/issues/9087)).
|
||||||
|
@ -545,7 +545,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
ImmutableList<RtspTrackTiming> trackTimingList =
|
ImmutableList<RtspTrackTiming> trackTimingList =
|
||||||
rtpInfoString == null
|
rtpInfoString == null
|
||||||
? ImmutableList.of()
|
? ImmutableList.of()
|
||||||
: RtspTrackTiming.parseTrackTiming(rtpInfoString);
|
: RtspTrackTiming.parseTrackTiming(rtpInfoString, uri);
|
||||||
onPlayResponseReceived(new RtspPlayResponse(response.status, timing, trackTimingList));
|
onPlayResponseReceived(new RtspPlayResponse(response.status, timing, trackTimingList));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -15,10 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.rtsp;
|
package com.google.android.exoplayer2.source.rtsp;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
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.UriUtil;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
@ -49,11 +54,12 @@ import com.google.common.collect.ImmutableList;
|
|||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param rtpInfoString The value of the RTP-Info header, with header name (RTP-Info) removed.
|
* @param rtpInfoString The value of the RTP-Info header, with header name (RTP-Info) removed.
|
||||||
|
* @param sessionUri The session URI, must include an {@code rtsp} scheme.
|
||||||
* @return A list of parsed {@link RtspTrackTiming}.
|
* @return A list of parsed {@link RtspTrackTiming}.
|
||||||
* @throws ParserException If parsing failed.
|
* @throws ParserException If parsing failed.
|
||||||
*/
|
*/
|
||||||
public static ImmutableList<RtspTrackTiming> parseTrackTiming(String rtpInfoString)
|
public static ImmutableList<RtspTrackTiming> parseTrackTiming(
|
||||||
throws ParserException {
|
String rtpInfoString, Uri sessionUri) throws ParserException {
|
||||||
|
|
||||||
ImmutableList.Builder<RtspTrackTiming> listBuilder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<RtspTrackTiming> listBuilder = new ImmutableList.Builder<>();
|
||||||
for (String perTrackTimingString : Util.split(rtpInfoString, ",")) {
|
for (String perTrackTimingString : Util.split(rtpInfoString, ",")) {
|
||||||
@ -69,7 +75,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
|
|
||||||
switch (attributeName) {
|
switch (attributeName) {
|
||||||
case "url":
|
case "url":
|
||||||
uri = Uri.parse(attributeValue);
|
uri = resolveUri(/* urlString= */ attributeValue, sessionUri);
|
||||||
break;
|
break;
|
||||||
case "seq":
|
case "seq":
|
||||||
sequenceNumber = Integer.parseInt(attributeValue);
|
sequenceNumber = Integer.parseInt(attributeValue);
|
||||||
@ -96,6 +102,48 @@ import com.google.common.collect.ImmutableList;
|
|||||||
return listBuilder.build();
|
return listBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the input string to always be an absolute URL with RTP-Info headers
|
||||||
|
*
|
||||||
|
* <p>Handles some servers do not send absolute URL in RTP-Info headers. This method takes in
|
||||||
|
* RTP-Info header's url string, and returns the correctly formatted {@link Uri url} for this
|
||||||
|
* track. The input url string could be
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>A correctly formatted URL, like "{@code rtsp://foo.bar/video}".
|
||||||
|
* <li>A correct URI that is missing the scheme, like "{@code foo.bar/video}".
|
||||||
|
* <li>A path to the resource, like "{@code video}" or "{@code /video}".
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param urlString The URL included in the RTP-Info header, without the {@code url=} identifier.
|
||||||
|
* @param sessionUri The session URI, must include an {@code rtsp} scheme, or {@link
|
||||||
|
* IllegalArgumentException} is thrown.
|
||||||
|
* @return The formatted URL.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
/* package */ static Uri resolveUri(String urlString, Uri sessionUri) {
|
||||||
|
checkArgument(checkNotNull(sessionUri.getScheme()).equals("rtsp"));
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(urlString);
|
||||||
|
if (uri.isAbsolute()) {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The urlString is at least missing the scheme.
|
||||||
|
uri = Uri.parse("rtsp://" + urlString);
|
||||||
|
String sessionUriString = sessionUri.toString();
|
||||||
|
|
||||||
|
String host = checkNotNull(uri.getHost());
|
||||||
|
if (host.equals(sessionUri.getHost())) {
|
||||||
|
// Handles the case that the urlString is only missing the scheme.
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionUriString.endsWith("/")
|
||||||
|
? UriUtil.resolveToUri(sessionUriString, urlString)
|
||||||
|
: UriUtil.resolveToUri(sessionUriString + "/", urlString);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timestamp of the next RTP packet, {@link C#TIME_UNSET} if not present.
|
* The timestamp of the next RTP packet, {@link C#TIME_UNSET} if not present.
|
||||||
*
|
*
|
||||||
|
@ -35,7 +35,7 @@ public class RtspTrackTimingTest {
|
|||||||
"url=rtsp://video.example.com/twister/video;seq=12312232;rtptime=78712811";
|
"url=rtsp://video.example.com/twister/video;seq=12312232;rtptime=78712811";
|
||||||
|
|
||||||
ImmutableList<RtspTrackTiming> trackTimingList =
|
ImmutableList<RtspTrackTiming> trackTimingList =
|
||||||
RtspTrackTiming.parseTrackTiming(rtpInfoString);
|
RtspTrackTiming.parseTrackTiming(rtpInfoString, Uri.parse("rtsp://video.example.com"));
|
||||||
|
|
||||||
assertThat(trackTimingList).hasSize(1);
|
assertThat(trackTimingList).hasSize(1);
|
||||||
RtspTrackTiming trackTiming = trackTimingList.get(0);
|
RtspTrackTiming trackTiming = trackTimingList.get(0);
|
||||||
@ -50,7 +50,7 @@ public class RtspTrackTimingTest {
|
|||||||
"url=rtsp://foo.com/bar.avi/streamid=0;seq=45102,url=rtsp://foo.com/bar.avi/streamid=1;seq=30211";
|
"url=rtsp://foo.com/bar.avi/streamid=0;seq=45102,url=rtsp://foo.com/bar.avi/streamid=1;seq=30211";
|
||||||
|
|
||||||
ImmutableList<RtspTrackTiming> trackTimingList =
|
ImmutableList<RtspTrackTiming> trackTimingList =
|
||||||
RtspTrackTiming.parseTrackTiming(rtpInfoString);
|
RtspTrackTiming.parseTrackTiming(rtpInfoString, Uri.parse("rtsp://foo.com"));
|
||||||
|
|
||||||
assertThat(trackTimingList).hasSize(2);
|
assertThat(trackTimingList).hasSize(2);
|
||||||
RtspTrackTiming trackTiming = trackTimingList.get(0);
|
RtspTrackTiming trackTiming = trackTimingList.get(0);
|
||||||
@ -67,27 +67,88 @@ public class RtspTrackTimingTest {
|
|||||||
public void parseTiming_withInvalidParameter_throws() {
|
public void parseTiming_withInvalidParameter_throws() {
|
||||||
String rtpInfoString = "url=rtsp://video.example.com/twister/video;seq=123abc";
|
String rtpInfoString = "url=rtsp://video.example.com/twister/video;seq=123abc";
|
||||||
|
|
||||||
assertThrows(ParserException.class, () -> RtspTrackTiming.parseTrackTiming(rtpInfoString));
|
assertThrows(
|
||||||
}
|
ParserException.class,
|
||||||
|
() ->
|
||||||
@Test
|
RtspTrackTiming.parseTrackTiming(
|
||||||
public void parseTiming_withInvalidUrl_throws() {
|
rtpInfoString, Uri.parse("rtsp://video.example.com/twister")));
|
||||||
String rtpInfoString = "url=video.example.com/twister/video;seq=36192348";
|
|
||||||
|
|
||||||
assertThrows(ParserException.class, () -> RtspTrackTiming.parseTrackTiming(rtpInfoString));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseTiming_withNoParameter_throws() {
|
public void parseTiming_withNoParameter_throws() {
|
||||||
String rtpInfoString = "url=rtsp://video.example.com/twister/video";
|
String rtpInfoString = "url=rtsp://video.example.com/twister/video";
|
||||||
|
|
||||||
assertThrows(ParserException.class, () -> RtspTrackTiming.parseTrackTiming(rtpInfoString));
|
assertThrows(
|
||||||
|
ParserException.class,
|
||||||
|
() ->
|
||||||
|
RtspTrackTiming.parseTrackTiming(
|
||||||
|
rtpInfoString, Uri.parse("rtsp://video.example.com/twister")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseTiming_withNoUrl_throws() {
|
public void parseTiming_withNoUrl_throws() {
|
||||||
String rtpInfoString = "seq=35421887";
|
String rtpInfoString = "seq=35421887";
|
||||||
|
|
||||||
assertThrows(ParserException.class, () -> RtspTrackTiming.parseTrackTiming(rtpInfoString));
|
assertThrows(
|
||||||
|
ParserException.class,
|
||||||
|
() ->
|
||||||
|
RtspTrackTiming.parseTrackTiming(
|
||||||
|
rtpInfoString, Uri.parse("rtsp://video.example.com/twister")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveUri_withAbsoluteUri_succeeds() {
|
||||||
|
Uri uri =
|
||||||
|
RtspTrackTiming.resolveUri(
|
||||||
|
"rtsp://video.example.com/twister/video=1?a2bfc09887ce",
|
||||||
|
Uri.parse("rtsp://video.example.com/twister"));
|
||||||
|
|
||||||
|
assertThat(uri).isEqualTo(Uri.parse("rtsp://video.example.com/twister/video=1?a2bfc09887ce"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveUri_withCompleteUriMissingScheme_succeeds() {
|
||||||
|
Uri uri =
|
||||||
|
RtspTrackTiming.resolveUri(
|
||||||
|
"video.example.com/twister/video=1", Uri.parse("rtsp://video.example.com/twister"));
|
||||||
|
|
||||||
|
assertThat(uri).isEqualTo(Uri.parse("rtsp://video.example.com/twister/video=1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveUri_withPartialUriMissingScheme_succeeds() {
|
||||||
|
Uri uri = RtspTrackTiming.resolveUri("video=1", Uri.parse("rtsp://video.example.com/twister"));
|
||||||
|
|
||||||
|
assertThat(uri).isEqualTo(Uri.parse("rtsp://video.example.com/twister/video=1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveUri_withMultipartPartialUriMissingScheme_succeeds() {
|
||||||
|
Uri uri =
|
||||||
|
RtspTrackTiming.resolveUri(
|
||||||
|
"container/video=1", Uri.parse("rtsp://video.example.com/twister"));
|
||||||
|
|
||||||
|
assertThat(uri).isEqualTo(Uri.parse("rtsp://video.example.com/twister/container/video=1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveUri_withPartialUriMissingSchemeWithIpBaseUri_succeeds() {
|
||||||
|
Uri uri = RtspTrackTiming.resolveUri("video=1", Uri.parse("rtsp://127.0.0.1:18888/test"));
|
||||||
|
|
||||||
|
assertThat(uri).isEqualTo(Uri.parse("rtsp://127.0.0.1:18888/test/video=1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveUri_withPartialUriMissingSchemeWithIpBaseUriWithSlash_succeeds() {
|
||||||
|
Uri uri = RtspTrackTiming.resolveUri("video=1", Uri.parse("rtsp://127.0.0.1:18888/test/"));
|
||||||
|
|
||||||
|
assertThat(uri).isEqualTo(Uri.parse("rtsp://127.0.0.1:18888/test/video=1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveUri_withSessionUriMissingScheme_throwsIllegalArgumentException() {
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> RtspTrackTiming.resolveUri("video=1", Uri.parse("127.0.0.1:18888/test")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user