move baseUrl from segments to representations: V2

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=138136090
This commit is contained in:
zhihuichen 2016-07-18 16:51:25 +01:00 committed by Oliver Woodman
parent 6c7ead5d0c
commit e2081f40fb
10 changed files with 204 additions and 139 deletions

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:yt="http://youtube.com/yt/2012/10/10" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT1.500S" profiles="urn:mpeg:dash:profile:isoff-main:2011" type="dynamic" availabilityStartTime="2016-10-14T17:00:17" timeShiftBufferDepth="PT7200.000S" minimumUpdatePeriod="PT2.000S" yt:earliestMediaSequence="0" yt:mpdRequestTime="2016-10-14T18:29:17.082" yt:mpdResponseTime="2016-10-14T18:29:17.194">
<Period start="PT0.000S" yt:segmentIngestTime="2016-10-14T17:00:14.257">
<SegmentTemplate startNumber="0" timescale="1000" media="sq/$Number$">
<SegmentTimeline>
<S d="2002" t="6009" r="2"/>
<S d="1985"/>
<S d="2000"/>
</SegmentTimeline>
</SegmentTemplate>
<AdaptationSet id="0" mimeType="audio/mp4" subsegmentAlignment="true">
<Role schemeIdUri="urn:mpeg:DASH:role:2011" value="main"/>
<Representation id="140" codecs="mp4a.40.2" audioSamplingRate="48000" startWithSAP="1" bandwidth="144000">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>http://redirector.googlevideo.com/videoplayback/id/BktsoMO3OMs.0/itag/140/source/yt_live_broadcast/ratebypass/yes/cmbypass/yes/mime/audio%2Fmp4/live/1/gir/yes/noclen/1/signature/B5137EA0CC278C07DD056D204E863CC81EDEB39E.1AD5D242EBC94922EDA7165353A89A5E08A4103A/key/dg_test0/mpd_version/5/ip/0.0.0.0/ipbits/0/expire/1476490914/sparams/ip,ipbits,expire,id,itag,source,ratebypass,cmbypass,mime,live,gir,noclen/</BaseURL>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" mimeType="video/mp4" subsegmentAlignment="true">
<Role schemeIdUri="urn:mpeg:DASH:role:2011" value="main"/>
<Representation id="133" codecs="avc1.4d4015" width="426" height="240" startWithSAP="1" maxPlayoutRate="1" bandwidth="258000" frameRate="30">
<BaseURL>http://redirector.googlevideo.com/videoplayback/id/BktsoMO3OMs.0/itag/133/source/yt_live_broadcast/ratebypass/yes/cmbypass/yes/mime/video%2Fmp4/live/1/gir/yes/noclen/1/signature/90154AE9C5C9D9D519CBF2E43AB0A1778375992D.40E2E855ADFB38FA7E95E168FEEEA6796B080BD7/key/dg_test0/mpd_version/5/ip/0.0.0.0/ipbits/0/expire/1476490914/sparams/ip,ipbits,expire,id,itag,source,ratebypass,cmbypass,mime,live,gir,noclen/</BaseURL>
</Representation>
<Representation id="134" codecs="avc1.4d401e" width="640" height="360" startWithSAP="1" maxPlayoutRate="1" bandwidth="646000" frameRate="30">
<BaseURL>http://redirector.googlevideo.com/videoplayback/id/BktsoMO3OMs.0/itag/134/source/yt_live_broadcast/ratebypass/yes/cmbypass/yes/mime/video%2Fmp4/live/1/gir/yes/noclen/1/signature/5C094AEFDCEB1A4D2F3C05F8BD095C336EF0E1C3.7AE6B9951B0237AAE6F031927AACAC4974BAFFAA/key/dg_test0/mpd_version/5/ip/0.0.0.0/ipbits/0/expire/1476490914/sparams/ip,ipbits,expire,id,itag,source,ratebypass,cmbypass,mime,live,gir,noclen/</BaseURL>
</Representation>
<Representation id="135" codecs="avc1.4d401f" width="854" height="480" startWithSAP="1" maxPlayoutRate="1" bandwidth="1171000" frameRate="30">
<BaseURL>http://redirector.googlevideo.com/videoplayback/id/BktsoMO3OMs.0/itag/135/source/yt_live_broadcast/ratebypass/yes/cmbypass/yes/mime/video%2Fmp4/live/1/gir/yes/noclen/1/signature/1F7660CA4E5B4AE4D60E18795680E34CDD2EF3C9.800B0A1D5F490DE142CCF4C88C64FD21D42129/key/dg_test0/mpd_version/5/ip/0.0.0.0/ipbits/0/expire/1476490914/sparams/ip,ipbits,expire,id,itag,source,ratebypass,cmbypass,mime,live,gir,noclen/</BaseURL>
</Representation>
<Representation id="160" codecs="avc1.42c00b" width="256" height="144" startWithSAP="1" maxPlayoutRate="1" bandwidth="124000" frameRate="30">
<BaseURL>http://redirector.googlevideo.com/videoplayback/id/BktsoMO3OMs.0/itag/160/source/yt_live_broadcast/ratebypass/yes/cmbypass/yes/mime/video%2Fmp4/live/1/gir/yes/noclen/1/signature/94EB61673784DF0C4237A1A866F2E171C8A64ADB.AEC00AA06C2278FEA8702FB62693B70D8977F46C/key/dg_test0/mpd_version/5/ip/0.0.0.0/ipbits/0/expire/1476490914/sparams/ip,ipbits,expire,id,itag,source,ratebypass,cmbypass,mime,live,gir,noclen/</BaseURL>
</Representation>
<Representation id="136" codecs="avc1.4d401f" width="1280" height="720" startWithSAP="1" maxPlayoutRate="1" bandwidth="2326000" frameRate="30">
<BaseURL>http://redirector.googlevideo.com/videoplayback/id/BktsoMO3OMs.0/itag/136/source/yt_live_broadcast/ratebypass/yes/cmbypass/yes/mime/video%2Fmp4/live/1/gir/yes/noclen/1/signature/6D8C34FC30A1F1A4F700B61180D1C4CCF6274844.29EBCB4A837DE626C52C66CF650519E61C2FF0BF/key/dg_test0/mpd_version/5/ip/0.0.0.0/ipbits/0/expire/1476490914/sparams/ip,ipbits,expire,id,itag,source,ratebypass,cmbypass,mime,live,gir,noclen/</BaseURL>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -28,6 +28,8 @@ public class DashManifestParserTest extends InstrumentationTestCase {
private static final String SAMPLE_MPD_1 = "dash/sample_mpd_1"; private static final String SAMPLE_MPD_1 = "dash/sample_mpd_1";
private static final String SAMPLE_MPD_2_UNKNOWN_MIME_TYPE = private static final String SAMPLE_MPD_2_UNKNOWN_MIME_TYPE =
"dash/sample_mpd_2_unknown_mime_type"; "dash/sample_mpd_2_unknown_mime_type";
private static final String SAMPLE_MPD_3_SEGMENT_TEMPLATE =
"dash/sample_mpd_3_segment_template";
/** /**
* Simple test to ensure the sample manifests parse without any exceptions being thrown. * Simple test to ensure the sample manifests parse without any exceptions being thrown.
@ -40,4 +42,30 @@ public class DashManifestParserTest extends InstrumentationTestCase {
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_2_UNKNOWN_MIME_TYPE)); TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_2_UNKNOWN_MIME_TYPE));
} }
public void testParseMediaPresentationDescriptionWithSegmentTemplate() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_3_SEGMENT_TEMPLATE));
assertEquals(1, mpd.getPeriodCount());
Period period = mpd.getPeriod(0);
assertNotNull(period);
assertEquals(2, period.adaptationSets.size());
for (AdaptationSet adaptationSet : period.adaptationSets) {
assertNotNull(adaptationSet);
for (Representation representation : adaptationSet.representations) {
if (representation instanceof Representation.MultiSegmentRepresentation) {
Representation.MultiSegmentRepresentation multiSegmentRepresentation =
(Representation.MultiSegmentRepresentation) representation;
int firstSegmentIndex = multiSegmentRepresentation.getFirstSegmentNum();
RangedUri uri = multiSegmentRepresentation.getSegmentUrl(firstSegmentIndex);
assertTrue(uri.resolveUriString(representation.baseUrl).contains(
"redirector.googlevideo.com"));
}
}
}
}
} }

View File

@ -23,56 +23,64 @@ import junit.framework.TestCase;
*/ */
public class RangedUriTest extends TestCase { public class RangedUriTest extends TestCase {
private static final String FULL_URI = "http://www.test.com/path/file.ext"; private static final String BASE_URI = "http://www.test.com/";
private static final String PARTIAL_URI = "path/file.ext";
private static final String FULL_URI = BASE_URI + PARTIAL_URI;
public void testMerge() { public void testMerge() {
RangedUri rangeA = new RangedUri(null, FULL_URI, 0, 10); RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
RangedUri rangeB = new RangedUri(null, FULL_URI, 10, 10); RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
RangedUri expected = new RangedUri(null, FULL_URI, 0, 20); RangedUri expected = new RangedUri(FULL_URI, 0, 20);
assertMerge(rangeA, rangeB, expected); assertMerge(rangeA, rangeB, expected, null);
} }
public void testMergeUnbounded() { public void testMergeUnbounded() {
RangedUri rangeA = new RangedUri(null, FULL_URI, 0, 10); RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
RangedUri rangeB = new RangedUri(null, FULL_URI, 10, C.LENGTH_UNSET); RangedUri rangeB = new RangedUri(FULL_URI, 10, C.LENGTH_UNSET);
RangedUri expected = new RangedUri(null, FULL_URI, 0, C.LENGTH_UNSET); RangedUri expected = new RangedUri(FULL_URI, 0, C.LENGTH_UNSET);
assertMerge(rangeA, rangeB, expected); assertMerge(rangeA, rangeB, expected, null);
} }
public void testNonMerge() { public void testNonMerge() {
// A and B do not overlap, so should not merge // A and B do not overlap, so should not merge
RangedUri rangeA = new RangedUri(null, FULL_URI, 0, 10); RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
RangedUri rangeB = new RangedUri(null, FULL_URI, 11, 10); RangedUri rangeB = new RangedUri(FULL_URI, 11, 10);
assertNonMerge(rangeA, rangeB); assertNonMerge(rangeA, rangeB, null);
// A and B do not overlap, so should not merge // A and B do not overlap, so should not merge
rangeA = new RangedUri(null, FULL_URI, 0, 10); rangeA = new RangedUri(FULL_URI, 0, 10);
rangeB = new RangedUri(null, FULL_URI, 11, C.LENGTH_UNSET); rangeB = new RangedUri(FULL_URI, 11, C.LENGTH_UNSET);
assertNonMerge(rangeA, rangeB); assertNonMerge(rangeA, rangeB, null);
// A and B are bounded but overlap, so should not merge // A and B are bounded but overlap, so should not merge
rangeA = new RangedUri(null, FULL_URI, 0, 11); rangeA = new RangedUri(FULL_URI, 0, 11);
rangeB = new RangedUri(null, FULL_URI, 10, 10); rangeB = new RangedUri(FULL_URI, 10, 10);
assertNonMerge(rangeA, rangeB); assertNonMerge(rangeA, rangeB, null);
// A and B overlap due to unboundedness, so should not merge // A and B overlap due to unboundedness, so should not merge
rangeA = new RangedUri(null, FULL_URI, 0, C.LENGTH_UNSET); rangeA = new RangedUri(FULL_URI, 0, C.LENGTH_UNSET);
rangeB = new RangedUri(null, FULL_URI, 10, C.LENGTH_UNSET); rangeB = new RangedUri(FULL_URI, 10, C.LENGTH_UNSET);
assertNonMerge(rangeA, rangeB); assertNonMerge(rangeA, rangeB, null);
} }
private void assertMerge(RangedUri rangeA, RangedUri rangeB, RangedUri expected) { public void testMergeWithBaseUri() {
RangedUri merged = rangeA.attemptMerge(rangeB); RangedUri rangeA = new RangedUri(PARTIAL_URI, 0, 10);
RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
RangedUri expected = new RangedUri(FULL_URI, 0, 20);
assertMerge(rangeA, rangeB, expected, BASE_URI);
}
private void assertMerge(RangedUri rangeA, RangedUri rangeB, RangedUri expected, String baseUrl) {
RangedUri merged = rangeA.attemptMerge(rangeB, baseUrl);
assertEquals(expected, merged); assertEquals(expected, merged);
merged = rangeB.attemptMerge(rangeA); merged = rangeB.attemptMerge(rangeA, baseUrl);
assertEquals(expected, merged); assertEquals(expected, merged);
} }
private void assertNonMerge(RangedUri rangeA, RangedUri rangeB) { private void assertNonMerge(RangedUri rangeA, RangedUri rangeB, String baseUrl) {
RangedUri merged = rangeA.attemptMerge(rangeB); RangedUri merged = rangeA.attemptMerge(rangeB, baseUrl);
assertNull(merged); assertNull(merged);
merged = rangeB.attemptMerge(rangeA); merged = rangeB.attemptMerge(rangeA, baseUrl);
assertNull(merged); assertNull(merged);
} }

View File

@ -27,16 +27,17 @@ public class RepresentationTest extends TestCase {
public void testGetCacheKey() { public void testGetCacheKey() {
String uri = "http://www.google.com"; String uri = "http://www.google.com";
SegmentBase base = new SingleSegmentBase(new RangedUri(uri, null, 0, 1), 1, 0, uri, 1, 1); SegmentBase base = new SingleSegmentBase(new RangedUri(null, 0, 1), 1, 0, 1, 1);
Format format = Format.createVideoContainerFormat("0", MimeTypes.APPLICATION_MP4, null, Format format = Format.createVideoContainerFormat("0", MimeTypes.APPLICATION_MP4, null,
MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null); MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null);
Representation representation = Representation.newInstance("test_stream_1", 3, format, base); Representation representation = Representation.newInstance("test_stream_1", 3, format, uri,
base);
assertEquals("test_stream_1.0.3", representation.getCacheKey()); assertEquals("test_stream_1.0.3", representation.getCacheKey());
format = Format.createVideoContainerFormat("150", MimeTypes.APPLICATION_MP4, null, format = Format.createVideoContainerFormat("150", MimeTypes.APPLICATION_MP4, null,
MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null); MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null);
representation = Representation.newInstance("test_stream_1", Representation.REVISION_ID_DEFAULT, representation = Representation.newInstance("test_stream_1", Representation.REVISION_ID_DEFAULT,
format, base); format, uri, base);
assertEquals("test_stream_1.150.-1", representation.getCacheKey()); assertEquals("test_stream_1.150.-1", representation.getCacheKey());
} }

View File

@ -25,7 +25,6 @@ import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
/* package */ final class DashWrappingSegmentIndex implements DashSegmentIndex { /* package */ final class DashWrappingSegmentIndex implements DashSegmentIndex {
private final ChunkIndex chunkIndex; private final ChunkIndex chunkIndex;
private final String uri;
/** /**
* @param chunkIndex The {@link ChunkIndex} to wrap. * @param chunkIndex The {@link ChunkIndex} to wrap.
@ -33,7 +32,6 @@ import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
*/ */
public DashWrappingSegmentIndex(ChunkIndex chunkIndex, String uri) { public DashWrappingSegmentIndex(ChunkIndex chunkIndex, String uri) {
this.chunkIndex = chunkIndex; this.chunkIndex = chunkIndex;
this.uri = uri;
} }
@Override @Override
@ -58,7 +56,7 @@ import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
@Override @Override
public RangedUri getSegmentUrl(int segmentNum) { public RangedUri getSegmentUrl(int segmentNum) {
return new RangedUri(uri, null, chunkIndex.offsets[segmentNum], chunkIndex.sizes[segmentNum]); return new RangedUri(null, chunkIndex.offsets[segmentNum], chunkIndex.sizes[segmentNum]);
} }
@Override @Override

View File

@ -288,18 +288,19 @@ public class DefaultDashChunkSource implements DashChunkSource {
DataSource dataSource, Format trackFormat, int trackSelectionReason, DataSource dataSource, Format trackFormat, int trackSelectionReason,
Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) { Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) {
RangedUri requestUri; RangedUri requestUri;
String baseUrl = representationHolder.representation.baseUrl;
if (initializationUri != null) { if (initializationUri != null) {
// It's common for initialization and index data to be stored adjacently. Attempt to merge // It's common for initialization and index data to be stored adjacently. Attempt to merge
// the two requests together to request both at once. // the two requests together to request both at once.
requestUri = initializationUri.attemptMerge(indexUri); requestUri = initializationUri.attemptMerge(indexUri, baseUrl);
if (requestUri == null) { if (requestUri == null) {
requestUri = initializationUri; requestUri = initializationUri;
} }
} else { } else {
requestUri = indexUri; requestUri = indexUri;
} }
DataSpec dataSpec = new DataSpec(requestUri.getUri(), requestUri.start, requestUri.length, DataSpec dataSpec = new DataSpec(requestUri.resolveUri(baseUrl), requestUri.start,
representationHolder.representation.getCacheKey()); requestUri.length, representationHolder.representation.getCacheKey());
return new InitializationChunk(dataSource, dataSpec, trackFormat, return new InitializationChunk(dataSource, dataSpec, trackFormat,
trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper); trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper);
} }
@ -311,8 +312,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
long startTimeUs = representationHolder.getSegmentStartTimeUs(segmentNum); long startTimeUs = representationHolder.getSegmentStartTimeUs(segmentNum);
long endTimeUs = representationHolder.getSegmentEndTimeUs(segmentNum); long endTimeUs = representationHolder.getSegmentEndTimeUs(segmentNum);
RangedUri segmentUri = representationHolder.getSegmentUrl(segmentNum); RangedUri segmentUri = representationHolder.getSegmentUrl(segmentNum);
DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length, DataSpec dataSpec = new DataSpec(segmentUri.resolveUri(representation.baseUrl),
representation.getCacheKey()); segmentUri.start, segmentUri.length, representation.getCacheKey());
if (representationHolder.extractorWrapper == null) { if (representationHolder.extractorWrapper == null) {
return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason,

View File

@ -205,11 +205,11 @@ public class DashManifestParser extends DefaultHandler
} else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) {
adaptationSets.add(parseAdaptationSet(xpp, baseUrl, segmentBase)); adaptationSets.add(parseAdaptationSet(xpp, baseUrl, segmentBase));
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, baseUrl, null); segmentBase = parseSegmentBase(xpp, null);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
segmentBase = parseSegmentList(xpp, baseUrl, null); segmentBase = parseSegmentList(xpp, null);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
segmentBase = parseSegmentTemplate(xpp, baseUrl, null); segmentBase = parseSegmentTemplate(xpp, null);
} }
} while (!XmlPullParserUtil.isEndTag(xpp, "Period")); } while (!XmlPullParserUtil.isEndTag(xpp, "Period"));
@ -263,11 +263,11 @@ public class DashManifestParser extends DefaultHandler
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
audioChannels = parseAudioChannelConfiguration(xpp); audioChannels = parseAudioChannelConfiguration(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, baseUrl, (SingleSegmentBase) segmentBase); segmentBase = parseSegmentBase(xpp, (SingleSegmentBase) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
segmentBase = parseSegmentList(xpp, baseUrl, (SegmentList) segmentBase); segmentBase = parseSegmentList(xpp, (SegmentList) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase); segmentBase = parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp)) { } else if (XmlPullParserUtil.isStartTag(xpp)) {
parseAdaptationSetChild(xpp); parseAdaptationSetChild(xpp);
} }
@ -390,11 +390,11 @@ public class DashManifestParser extends DefaultHandler
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
audioChannels = parseAudioChannelConfiguration(xpp); audioChannels = parseAudioChannelConfiguration(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, baseUrl, (SingleSegmentBase) segmentBase); segmentBase = parseSegmentBase(xpp, (SingleSegmentBase) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
segmentBase = parseSegmentList(xpp, baseUrl, (SegmentList) segmentBase); segmentBase = parseSegmentList(xpp, (SegmentList) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase); segmentBase = parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) { } else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
SchemeData contentProtection = parseContentProtection(xpp); SchemeData contentProtection = parseContentProtection(xpp);
if (contentProtection != null) { if (contentProtection != null) {
@ -407,7 +407,7 @@ public class DashManifestParser extends DefaultHandler
audioSamplingRate, bandwidth, adaptationSetLanguage, codecs); audioSamplingRate, bandwidth, adaptationSetLanguage, codecs);
segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl); segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl);
return new RepresentationInfo(format, segmentBase, drmSchemeDatas); return new RepresentationInfo(format, baseUrl, segmentBase, drmSchemeDatas);
} }
protected Format buildFormat(String id, String containerMimeType, int width, int height, protected Format buildFormat(String id, String containerMimeType, int width, int height,
@ -441,13 +441,13 @@ public class DashManifestParser extends DefaultHandler
format = format.copyWithDrmInitData(new DrmInitData(drmSchemeDatas)); format = format.copyWithDrmInitData(new DrmInitData(drmSchemeDatas));
} }
return Representation.newInstance(contentId, Representation.REVISION_ID_DEFAULT, format, return Representation.newInstance(contentId, Representation.REVISION_ID_DEFAULT, format,
representationInfo.segmentBase); representationInfo.baseUrl, representationInfo.segmentBase);
} }
// SegmentBase, SegmentList and SegmentTemplate parsing. // SegmentBase, SegmentList and SegmentTemplate parsing.
protected SingleSegmentBase parseSegmentBase(XmlPullParser xpp, String baseUrl, protected SingleSegmentBase parseSegmentBase(XmlPullParser xpp, SingleSegmentBase parent)
SingleSegmentBase parent) throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1); long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset", long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset",
@ -466,21 +466,21 @@ public class DashManifestParser extends DefaultHandler
do { do {
xpp.next(); xpp.next();
if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) { if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
initialization = parseInitialization(xpp, baseUrl); initialization = parseInitialization(xpp);
} }
} while (!XmlPullParserUtil.isEndTag(xpp, "SegmentBase")); } while (!XmlPullParserUtil.isEndTag(xpp, "SegmentBase"));
return buildSingleSegmentBase(initialization, timescale, presentationTimeOffset, baseUrl, return buildSingleSegmentBase(initialization, timescale, presentationTimeOffset, indexStart,
indexStart, indexLength); indexLength);
} }
protected SingleSegmentBase buildSingleSegmentBase(RangedUri initialization, long timescale, protected SingleSegmentBase buildSingleSegmentBase(RangedUri initialization, long timescale,
long presentationTimeOffset, String baseUrl, long indexStart, long indexLength) { long presentationTimeOffset, long indexStart, long indexLength) {
return new SingleSegmentBase(initialization, timescale, presentationTimeOffset, baseUrl, return new SingleSegmentBase(initialization, timescale, presentationTimeOffset, indexStart,
indexStart, indexLength); indexLength);
} }
protected SegmentList parseSegmentList(XmlPullParser xpp, String baseUrl, SegmentList parent) protected SegmentList parseSegmentList(XmlPullParser xpp, SegmentList parent)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1); long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
@ -496,14 +496,14 @@ public class DashManifestParser extends DefaultHandler
do { do {
xpp.next(); xpp.next();
if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) { if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
initialization = parseInitialization(xpp, baseUrl); initialization = parseInitialization(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) {
timeline = parseSegmentTimeline(xpp); timeline = parseSegmentTimeline(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentURL")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentURL")) {
if (segments == null) { if (segments == null) {
segments = new ArrayList<>(); segments = new ArrayList<>();
} }
segments.add(parseSegmentUrl(xpp, baseUrl)); segments.add(parseSegmentUrl(xpp));
} }
} while (!XmlPullParserUtil.isEndTag(xpp, "SegmentList")); } while (!XmlPullParserUtil.isEndTag(xpp, "SegmentList"));
@ -524,8 +524,8 @@ public class DashManifestParser extends DefaultHandler
startNumber, duration, timeline, segments); startNumber, duration, timeline, segments);
} }
protected SegmentTemplate parseSegmentTemplate(XmlPullParser xpp, String baseUrl, protected SegmentTemplate parseSegmentTemplate(XmlPullParser xpp, SegmentTemplate parent)
SegmentTemplate parent) throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1); long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset", long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset",
parent != null ? parent.presentationTimeOffset : 0); parent != null ? parent.presentationTimeOffset : 0);
@ -542,7 +542,7 @@ public class DashManifestParser extends DefaultHandler
do { do {
xpp.next(); xpp.next();
if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) { if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
initialization = parseInitialization(xpp, baseUrl); initialization = parseInitialization(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) {
timeline = parseSegmentTimeline(xpp); timeline = parseSegmentTimeline(xpp);
} }
@ -554,15 +554,15 @@ public class DashManifestParser extends DefaultHandler
} }
return buildSegmentTemplate(initialization, timescale, presentationTimeOffset, return buildSegmentTemplate(initialization, timescale, presentationTimeOffset,
startNumber, duration, timeline, initializationTemplate, mediaTemplate, baseUrl); startNumber, duration, timeline, initializationTemplate, mediaTemplate);
} }
protected SegmentTemplate buildSegmentTemplate(RangedUri initialization, long timescale, protected SegmentTemplate buildSegmentTemplate(RangedUri initialization, long timescale,
long presentationTimeOffset, int startNumber, long duration, long presentationTimeOffset, int startNumber, long duration,
List<SegmentTimelineElement> timeline, UrlTemplate initializationTemplate, List<SegmentTimelineElement> timeline, UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate, String baseUrl) { UrlTemplate mediaTemplate) {
return new SegmentTemplate(initialization, timescale, presentationTimeOffset, return new SegmentTemplate(initialization, timescale, presentationTimeOffset,
startNumber, duration, timeline, initializationTemplate, mediaTemplate, baseUrl); startNumber, duration, timeline, initializationTemplate, mediaTemplate);
} }
protected List<SegmentTimelineElement> parseSegmentTimeline(XmlPullParser xpp) protected List<SegmentTimelineElement> parseSegmentTimeline(XmlPullParser xpp)
@ -597,15 +597,15 @@ public class DashManifestParser extends DefaultHandler
return defaultValue; return defaultValue;
} }
protected RangedUri parseInitialization(XmlPullParser xpp, String baseUrl) { protected RangedUri parseInitialization(XmlPullParser xpp) {
return parseRangedUrl(xpp, baseUrl, "sourceURL", "range"); return parseRangedUrl(xpp, "sourceURL", "range");
} }
protected RangedUri parseSegmentUrl(XmlPullParser xpp, String baseUrl) { protected RangedUri parseSegmentUrl(XmlPullParser xpp) {
return parseRangedUrl(xpp, baseUrl, "media", "mediaRange"); return parseRangedUrl(xpp, "media", "mediaRange");
} }
protected RangedUri parseRangedUrl(XmlPullParser xpp, String baseUrl, String urlAttribute, protected RangedUri parseRangedUrl(XmlPullParser xpp, String urlAttribute,
String rangeAttribute) { String rangeAttribute) {
String urlText = xpp.getAttributeValue(null, urlAttribute); String urlText = xpp.getAttributeValue(null, urlAttribute);
long rangeStart = 0; long rangeStart = 0;
@ -618,12 +618,11 @@ public class DashManifestParser extends DefaultHandler
rangeLength = Long.parseLong(rangeTextArray[1]) - rangeStart + 1; rangeLength = Long.parseLong(rangeTextArray[1]) - rangeStart + 1;
} }
} }
return buildRangedUri(baseUrl, urlText, rangeStart, rangeLength); return buildRangedUri(urlText, rangeStart, rangeLength);
} }
protected RangedUri buildRangedUri(String baseUrl, String urlText, long rangeStart, protected RangedUri buildRangedUri(String urlText, long rangeStart, long rangeLength) {
long rangeLength) { return new RangedUri(urlText, rangeStart, rangeLength);
return new RangedUri(baseUrl, urlText, rangeStart, rangeLength);
} }
// AudioChannelConfiguration parsing. // AudioChannelConfiguration parsing.
@ -788,12 +787,14 @@ public class DashManifestParser extends DefaultHandler
private static final class RepresentationInfo { private static final class RepresentationInfo {
public final Format format; public final Format format;
public final String baseUrl;
public final SegmentBase segmentBase; public final SegmentBase segmentBase;
public final ArrayList<SchemeData> drmSchemeDatas; public final ArrayList<SchemeData> drmSchemeDatas;
public RepresentationInfo(Format format, SegmentBase segmentBase, public RepresentationInfo(Format format, String baseUrl, SegmentBase segmentBase,
ArrayList<SchemeData> drmSchemeDatas) { ArrayList<SchemeData> drmSchemeDatas) {
this.format = format; this.format = format;
this.baseUrl = baseUrl;
this.segmentBase = segmentBase; this.segmentBase = segmentBase;
this.drmSchemeDatas = drmSchemeDatas; this.drmSchemeDatas = drmSchemeDatas;
} }

View File

@ -17,11 +17,10 @@ package com.google.android.exoplayer2.source.dash.manifest;
import android.net.Uri; import android.net.Uri;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.UriUtil; import com.google.android.exoplayer2.util.UriUtil;
/** /**
* Defines a range of data located at a {@link Uri}. * Defines a range of data located at a reference uri.
*/ */
public final class RangedUri { public final class RangedUri {
@ -35,12 +34,6 @@ public final class RangedUri {
*/ */
public final long length; public final long length;
// The URI is stored internally in two parts: reference URI and a base URI to use when
// resolving it. This helps optimize memory usage in the same way that DASH manifests allow many
// URLs to be expressed concisely in the form of a single BaseURL and many relative paths. Note
// that this optimization relies on the same object being passed as the base URI to many
// instances of this class.
private final String baseUri;
private final String referenceUri; private final String referenceUri;
private int hashCode; private int hashCode;
@ -48,57 +41,59 @@ public final class RangedUri {
/** /**
* Constructs an ranged uri. * Constructs an ranged uri.
* *
* @param baseUri A uri that can form the base of the uri defined by the instance. * @param referenceUri The reference uri.
* @param referenceUri A reference uri that should be resolved with respect to {@code baseUri}.
* @param start The (zero based) index of the first byte of the range. * @param start The (zero based) index of the first byte of the range.
* @param length The length of the range, or {@link C#LENGTH_UNSET} to indicate that the range is * @param length The length of the range, or {@link C#LENGTH_UNSET} to indicate that the range is
* unbounded. * unbounded.
*/ */
public RangedUri(String baseUri, String referenceUri, long start, long length) { public RangedUri(String referenceUri, long start, long length) {
Assertions.checkArgument(baseUri != null || referenceUri != null); this.referenceUri = referenceUri == null ? "" : referenceUri;
this.baseUri = baseUri;
this.referenceUri = referenceUri;
this.start = start; this.start = start;
this.length = length; this.length = length;
} }
/** /**
* Returns the {@link Uri} represented by the instance. * Returns the resolved {@link Uri} represented by the instance.
* *
* @param baseUri The base Uri.
* @return The {@link Uri} represented by the instance. * @return The {@link Uri} represented by the instance.
*/ */
public Uri getUri() { public Uri resolveUri(String baseUri) {
return UriUtil.resolveToUri(baseUri, referenceUri); return UriUtil.resolveToUri(baseUri, referenceUri);
} }
/** /**
* Returns the uri represented by the instance as a string. * Returns the resolved uri represented by the instance as a string.
* *
* @param baseUri The base Uri.
* @return The uri represented by the instance. * @return The uri represented by the instance.
*/ */
public String getUriString() { public String resolveUriString(String baseUri) {
return UriUtil.resolve(baseUri, referenceUri); return UriUtil.resolve(baseUri, referenceUri);
} }
/** /**
* Attempts to merge this {@link RangedUri} with another. * Attempts to merge this {@link RangedUri} with another and an optional common base uri.
* <p> * <p>
* A merge is successful if both instances define the same {@link Uri}, and if one starts the byte * A merge is successful if both instances define the same {@link Uri} after resolution with the
* after the other ends, forming a contiguous region with no overlap. * base uri, and if one starts the byte after the other ends, forming a contiguous region with
* no overlap.
* <p> * <p>
* If {@code other} is null then the merge is considered unsuccessful, and null is returned. * If {@code other} is null then the merge is considered unsuccessful, and null is returned.
* *
* @param other The {@link RangedUri} to merge. * @param other The {@link RangedUri} to merge.
* @param baseUri The optional base Uri.
* @return The merged {@link RangedUri} if the merge was successful. Null otherwise. * @return The merged {@link RangedUri} if the merge was successful. Null otherwise.
*/ */
public RangedUri attemptMerge(RangedUri other) { public RangedUri attemptMerge(RangedUri other, String baseUri) {
if (other == null || !getUriString().equals(other.getUriString())) { final String resolvedUri = resolveUriString(baseUri);
if (other == null || !resolvedUri.equals(other.resolveUriString(baseUri))) {
return null; return null;
} else if (length != C.LENGTH_UNSET && start + length == other.start) { } else if (length != C.LENGTH_UNSET && start + length == other.start) {
return new RangedUri(baseUri, referenceUri, start, return new RangedUri(resolvedUri, start,
other.length == C.LENGTH_UNSET ? C.LENGTH_UNSET : length + other.length); other.length == C.LENGTH_UNSET ? C.LENGTH_UNSET : length + other.length);
} else if (other.length != C.LENGTH_UNSET && other.start + other.length == start) { } else if (other.length != C.LENGTH_UNSET && other.start + other.length == start) {
return new RangedUri(baseUri, referenceUri, other.start, return new RangedUri(resolvedUri, other.start,
length == C.LENGTH_UNSET ? C.LENGTH_UNSET : other.length + length); length == C.LENGTH_UNSET ? C.LENGTH_UNSET : other.length + length);
} else { } else {
return null; return null;
@ -111,7 +106,7 @@ public final class RangedUri {
int result = 17; int result = 17;
result = 31 * result + (int) start; result = 31 * result + (int) start;
result = 31 * result + (int) length; result = 31 * result + (int) length;
result = 31 * result + getUriString().hashCode(); result = 31 * result + referenceUri.hashCode();
hashCode = result; hashCode = result;
} }
return hashCode; return hashCode;
@ -128,7 +123,7 @@ public final class RangedUri {
RangedUri other = (RangedUri) obj; RangedUri other = (RangedUri) obj;
return this.start == other.start return this.start == other.start
&& this.length == other.length && this.length == other.length
&& getUriString().equals(other.getUriString()); && referenceUri.equals(other.referenceUri);
} }
} }

View File

@ -52,6 +52,10 @@ public abstract class Representation {
* The format of the representation. * The format of the representation.
*/ */
public final Format format; public final Format format;
/**
* The base URL of the representation.
*/
public final String baseUrl;
/** /**
* The offset of the presentation timestamps in the media stream relative to media time. * The offset of the presentation timestamps in the media stream relative to media time.
*/ */
@ -65,12 +69,13 @@ public abstract class Representation {
* @param contentId Identifies the piece of content to which this representation belongs. * @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content. * @param revisionId Identifies the revision of the content.
* @param format The format of the representation. * @param format The format of the representation.
* @param baseUrl The base URL.
* @param segmentBase A segment base element for the representation. * @param segmentBase A segment base element for the representation.
* @return The constructed instance. * @return The constructed instance.
*/ */
public static Representation newInstance(String contentId, long revisionId, Format format, public static Representation newInstance(String contentId, long revisionId, Format format,
SegmentBase segmentBase) { String baseUrl, SegmentBase segmentBase) {
return newInstance(contentId, revisionId, format, segmentBase, null); return newInstance(contentId, revisionId, format, baseUrl, segmentBase, null);
} }
/** /**
@ -79,18 +84,19 @@ public abstract class Representation {
* @param contentId Identifies the piece of content to which this representation belongs. * @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content. * @param revisionId Identifies the revision of the content.
* @param format The format of the representation. * @param format The format of the representation.
* @param baseUrl The base URL of the representation.
* @param segmentBase A segment base element for the representation. * @param segmentBase A segment base element for the representation.
* @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null. This * @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null. This
* parameter is ignored if {@code segmentBase} consists of multiple segments. * parameter is ignored if {@code segmentBase} consists of multiple segments.
* @return The constructed instance. * @return The constructed instance.
*/ */
public static Representation newInstance(String contentId, long revisionId, Format format, public static Representation newInstance(String contentId, long revisionId, Format format,
SegmentBase segmentBase, String customCacheKey) { String baseUrl, SegmentBase segmentBase, String customCacheKey) {
if (segmentBase instanceof SingleSegmentBase) { if (segmentBase instanceof SingleSegmentBase) {
return new SingleSegmentRepresentation(contentId, revisionId, format, return new SingleSegmentRepresentation(contentId, revisionId, format, baseUrl,
(SingleSegmentBase) segmentBase, customCacheKey, C.LENGTH_UNSET); (SingleSegmentBase) segmentBase, customCacheKey, C.LENGTH_UNSET);
} else if (segmentBase instanceof MultiSegmentBase) { } else if (segmentBase instanceof MultiSegmentBase) {
return new MultiSegmentRepresentation(contentId, revisionId, format, return new MultiSegmentRepresentation(contentId, revisionId, format, baseUrl,
(MultiSegmentBase) segmentBase); (MultiSegmentBase) segmentBase);
} else { } else {
throw new IllegalArgumentException("segmentBase must be of type SingleSegmentBase or " throw new IllegalArgumentException("segmentBase must be of type SingleSegmentBase or "
@ -98,11 +104,12 @@ public abstract class Representation {
} }
} }
private Representation(String contentId, long revisionId, Format format, private Representation(String contentId, long revisionId, Format format, String baseUrl,
SegmentBase segmentBase) { SegmentBase segmentBase) {
this.contentId = contentId; this.contentId = contentId;
this.revisionId = revisionId; this.revisionId = revisionId;
this.format = format; this.format = format;
this.baseUrl = baseUrl;
initializationUri = segmentBase.getInitialization(this); initializationUri = segmentBase.getInitialization(this);
presentationTimeOffsetUs = segmentBase.getPresentationTimeOffsetUs(); presentationTimeOffsetUs = segmentBase.getPresentationTimeOffsetUs();
} }
@ -166,26 +173,27 @@ public abstract class Representation {
public static SingleSegmentRepresentation newInstance(String contentId, long revisionId, public static SingleSegmentRepresentation newInstance(String contentId, long revisionId,
Format format, String uri, long initializationStart, long initializationEnd, Format format, String uri, long initializationStart, long initializationEnd,
long indexStart, long indexEnd, String customCacheKey, long contentLength) { long indexStart, long indexEnd, String customCacheKey, long contentLength) {
RangedUri rangedUri = new RangedUri(uri, null, initializationStart, RangedUri rangedUri = new RangedUri(null, initializationStart,
initializationEnd - initializationStart + 1); initializationEnd - initializationStart + 1);
SingleSegmentBase segmentBase = new SingleSegmentBase(rangedUri, 1, 0, uri, indexStart, SingleSegmentBase segmentBase = new SingleSegmentBase(rangedUri, 1, 0, indexStart,
indexEnd - indexStart + 1); indexEnd - indexStart + 1);
return new SingleSegmentRepresentation(contentId, revisionId, return new SingleSegmentRepresentation(contentId, revisionId,
format, segmentBase, customCacheKey, contentLength); format, uri, segmentBase, customCacheKey, contentLength);
} }
/** /**
* @param contentId Identifies the piece of content to which this representation belongs. * @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content. * @param revisionId Identifies the revision of the content.
* @param format The format of the representation. * @param format The format of the representation.
* @param baseUrl The base URL of the representation.
* @param segmentBase The segment base underlying the representation. * @param segmentBase The segment base underlying the representation.
* @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null. * @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null.
* @param contentLength The content length, or {@link C#LENGTH_UNSET} if unknown. * @param contentLength The content length, or {@link C#LENGTH_UNSET} if unknown.
*/ */
public SingleSegmentRepresentation(String contentId, long revisionId, Format format, public SingleSegmentRepresentation(String contentId, long revisionId, Format format,
SingleSegmentBase segmentBase, String customCacheKey, long contentLength) { String baseUrl, SingleSegmentBase segmentBase, String customCacheKey, long contentLength) {
super(contentId, revisionId, format, segmentBase); super(contentId, revisionId, format, baseUrl, segmentBase);
this.uri = Uri.parse(segmentBase.uri); this.uri = Uri.parse(baseUrl);
this.indexUri = segmentBase.getIndex(); this.indexUri = segmentBase.getIndex();
this.cacheKey = customCacheKey != null ? customCacheKey this.cacheKey = customCacheKey != null ? customCacheKey
: contentId != null ? contentId + "." + format.id + "." + revisionId : null; : contentId != null ? contentId + "." + format.id + "." + revisionId : null;
@ -193,7 +201,7 @@ public abstract class Representation {
// If we have an index uri then the index is defined externally, and we shouldn't return one // If we have an index uri then the index is defined externally, and we shouldn't return one
// directly. If we don't, then we can't do better than an index defining a single segment. // directly. If we don't, then we can't do better than an index defining a single segment.
segmentIndex = indexUri != null ? null segmentIndex = indexUri != null ? null
: new SingleSegmentIndex(new RangedUri(segmentBase.uri, null, 0, contentLength)); : new SingleSegmentIndex(new RangedUri(null, 0, contentLength));
} }
@Override @Override
@ -225,11 +233,12 @@ public abstract class Representation {
* @param contentId Identifies the piece of content to which this representation belongs. * @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content. * @param revisionId Identifies the revision of the content.
* @param format The format of the representation. * @param format The format of the representation.
* @param baseUrl The base URL of the representation.
* @param segmentBase The segment base underlying the representation. * @param segmentBase The segment base underlying the representation.
*/ */
public MultiSegmentRepresentation(String contentId, long revisionId, Format format, public MultiSegmentRepresentation(String contentId, long revisionId, Format format,
MultiSegmentBase segmentBase) { String baseUrl, MultiSegmentBase segmentBase) {
super(contentId, revisionId, format, segmentBase); super(contentId, revisionId, format, baseUrl, segmentBase);
this.segmentBase = segmentBase; this.segmentBase = segmentBase;
} }

View File

@ -65,11 +65,6 @@ public abstract class SegmentBase {
*/ */
public static class SingleSegmentBase extends SegmentBase { public static class SingleSegmentBase extends SegmentBase {
/**
* The uri of the segment.
*/
public final String uri;
/* package */ final long indexStart; /* package */ final long indexStart;
/* package */ final long indexLength; /* package */ final long indexLength;
@ -79,27 +74,22 @@ public abstract class SegmentBase {
* @param timescale The timescale in units per second. * @param timescale The timescale in units per second.
* @param presentationTimeOffset The presentation time offset. The value in seconds is the * @param presentationTimeOffset The presentation time offset. The value in seconds is the
* division of this value and {@code timescale}. * division of this value and {@code timescale}.
* @param uri The uri of the segment.
* @param indexStart The byte offset of the index data in the segment. * @param indexStart The byte offset of the index data in the segment.
* @param indexLength The length of the index data in bytes. * @param indexLength The length of the index data in bytes.
*/ */
public SingleSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset, public SingleSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset,
String uri, long indexStart, long indexLength) { long indexStart, long indexLength) {
super(initialization, timescale, presentationTimeOffset); super(initialization, timescale, presentationTimeOffset);
this.uri = uri;
this.indexStart = indexStart; this.indexStart = indexStart;
this.indexLength = indexLength; this.indexLength = indexLength;
} }
/**
* @param uri The uri of the segment.
*/
public SingleSegmentBase(String uri) { public SingleSegmentBase(String uri) {
this(null, 1, 0, uri, 0, 0); this(null, 1, 0, 0, 0);
} }
public RangedUri getIndex() { public RangedUri getIndex() {
return indexLength <= 0 ? null : new RangedUri(uri, null, indexStart, indexLength); return indexLength <= 0 ? null : new RangedUri(null, indexStart, indexLength);
} }
} }
@ -279,8 +269,6 @@ public abstract class SegmentBase {
/* package */ final UrlTemplate initializationTemplate; /* package */ final UrlTemplate initializationTemplate;
/* package */ final UrlTemplate mediaTemplate; /* package */ final UrlTemplate mediaTemplate;
private final String baseUrl;
/** /**
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data * @param initialization A {@link RangedUri} corresponding to initialization data, if such data
* exists. The value of this parameter is ignored if {@code initializationTemplate} is * exists. The value of this parameter is ignored if {@code initializationTemplate} is
@ -299,16 +287,14 @@ public abstract class SegmentBase {
* such data exists. If non-null then the {@code initialization} parameter is ignored. If * such data exists. If non-null then the {@code initialization} parameter is ignored. If
* null then {@code initialization} will be used. * null then {@code initialization} will be used.
* @param mediaTemplate A template defining the location of each media segment. * @param mediaTemplate A template defining the location of each media segment.
* @param baseUrl A url to use as the base for relative urls generated by the templates.
*/ */
public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset, public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset,
int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline, int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline,
UrlTemplate initializationTemplate, UrlTemplate mediaTemplate, String baseUrl) { UrlTemplate initializationTemplate, UrlTemplate mediaTemplate) {
super(initialization, timescale, presentationTimeOffset, startNumber, super(initialization, timescale, presentationTimeOffset, startNumber,
duration, segmentTimeline); duration, segmentTimeline);
this.initializationTemplate = initializationTemplate; this.initializationTemplate = initializationTemplate;
this.mediaTemplate = mediaTemplate; this.mediaTemplate = mediaTemplate;
this.baseUrl = baseUrl;
} }
@Override @Override
@ -316,7 +302,7 @@ public abstract class SegmentBase {
if (initializationTemplate != null) { if (initializationTemplate != null) {
String urlString = initializationTemplate.buildUri(representation.format.id, 0, String urlString = initializationTemplate.buildUri(representation.format.id, 0,
representation.format.bitrate, 0); representation.format.bitrate, 0);
return new RangedUri(baseUrl, urlString, 0, C.LENGTH_UNSET); return new RangedUri(urlString, 0, C.LENGTH_UNSET);
} else { } else {
return super.getInitialization(representation); return super.getInitialization(representation);
} }
@ -332,7 +318,7 @@ public abstract class SegmentBase {
} }
String uriString = mediaTemplate.buildUri(representation.format.id, sequenceNumber, String uriString = mediaTemplate.buildUri(representation.format.id, sequenceNumber,
representation.format.bitrate, time); representation.format.bitrate, time);
return new RangedUri(baseUrl, uriString, 0, C.LENGTH_UNSET); return new RangedUri(uriString, 0, C.LENGTH_UNSET);
} }
@Override @Override