use of androidx.media3.exoplayer.dash.manifest.Representation in project media by androidx.
the class DashManifestParserTest method parseEssentialAndSupplementalProperties.
@Test
public void parseEssentialAndSupplementalProperties() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest manifest = parser.parse(Uri.parse("https://example.com/test.mpd"), TestUtil.getInputStream(ApplicationProvider.getApplicationContext(), SAMPLE_MPD_ESSENTIAL_SUPPLEMENTAL_PROPERTIES));
// Verify test setup.
assertThat(manifest.getPeriodCount()).isEqualTo(1);
assertThat(manifest.getPeriod(0).adaptationSets).hasSize(1);
AdaptationSet adaptationSet = manifest.getPeriod(0).adaptationSets.get(0);
assertThat(adaptationSet.representations).hasSize(2);
Representation representation0 = adaptationSet.representations.get(0);
Representation representation1 = adaptationSet.representations.get(1);
assertThat(representation0).isInstanceOf(SingleSegmentRepresentation.class);
assertThat(representation1).isInstanceOf(MultiSegmentRepresentation.class);
// Verify parsed properties.
assertThat(adaptationSet.essentialProperties).hasSize(1);
assertThat(adaptationSet.essentialProperties.get(0).schemeIdUri).isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(adaptationSet.essentialProperties.get(0).value).isEqualTo("adaptationEssential");
assertThat(adaptationSet.supplementalProperties).hasSize(1);
assertThat(adaptationSet.supplementalProperties.get(0).schemeIdUri).isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(adaptationSet.supplementalProperties.get(0).value).isEqualTo("adaptationSupplemental");
assertThat(representation0.essentialProperties).hasSize(2);
assertThat(representation0.essentialProperties.get(0).schemeIdUri).isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation0.essentialProperties.get(0).value).isEqualTo("adaptationEssential");
assertThat(representation0.essentialProperties.get(1).schemeIdUri).isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation0.essentialProperties.get(1).value).isEqualTo("representationEssential");
assertThat(representation0.supplementalProperties).hasSize(2);
assertThat(representation0.supplementalProperties.get(0).schemeIdUri).isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation0.supplementalProperties.get(0).value).isEqualTo("adaptationSupplemental");
assertThat(representation0.supplementalProperties.get(1).schemeIdUri).isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation0.supplementalProperties.get(1).value).isEqualTo("representationSupplemental");
assertThat(representation1.essentialProperties).hasSize(2);
assertThat(representation0.essentialProperties.get(0).schemeIdUri).isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation0.essentialProperties.get(0).value).isEqualTo("adaptationEssential");
assertThat(representation1.essentialProperties.get(1).schemeIdUri).isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation1.essentialProperties.get(1).value).isEqualTo("representationEssential");
assertThat(representation1.supplementalProperties).hasSize(2);
assertThat(representation0.supplementalProperties.get(0).schemeIdUri).isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation0.supplementalProperties.get(0).value).isEqualTo("adaptationSupplemental");
assertThat(representation1.supplementalProperties.get(1).schemeIdUri).isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation1.supplementalProperties.get(1).value).isEqualTo("representationSupplemental");
}
use of androidx.media3.exoplayer.dash.manifest.Representation in project media by androidx.
the class DashMediaSource method getIntervalUntilNextManifestRefreshMs.
private static long getIntervalUntilNextManifestRefreshMs(DashManifest manifest, long nowUnixTimeMs) {
int periodIndex = manifest.getPeriodCount() - 1;
Period period = manifest.getPeriod(periodIndex);
long periodStartUs = Util.msToUs(period.startMs);
long periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
long nowUnixTimeUs = Util.msToUs(nowUnixTimeMs);
long availabilityStartTimeUs = Util.msToUs(manifest.availabilityStartTimeMs);
long intervalUs = Util.msToUs(DEFAULT_NOTIFY_MANIFEST_INTERVAL_MS);
for (int i = 0; i < period.adaptationSets.size(); i++) {
List<Representation> representations = period.adaptationSets.get(i).representations;
if (representations.isEmpty()) {
continue;
}
@Nullable DashSegmentIndex index = representations.get(0).getIndex();
if (index != null) {
long nextSegmentShiftUnixTimeUs = availabilityStartTimeUs + periodStartUs + index.getNextSegmentAvailableTimeUs(periodDurationUs, nowUnixTimeUs);
long requiredIntervalUs = nextSegmentShiftUnixTimeUs - nowUnixTimeUs;
// Avoid multiple refreshes within a very small amount of time.
if (requiredIntervalUs < intervalUs - 100_000 || (requiredIntervalUs > intervalUs && requiredIntervalUs < intervalUs + 100_000)) {
intervalUs = requiredIntervalUs;
}
}
}
// Round up to compensate for a potential loss in the us to ms conversion.
return LongMath.divide(intervalUs, 1000, RoundingMode.CEILING);
}
use of androidx.media3.exoplayer.dash.manifest.Representation in project media by androidx.
the class DashMediaSource method getAvailableEndTimeInManifestUs.
private static long getAvailableEndTimeInManifestUs(Period period, long periodDurationUs, long nowUnixTimeUs) {
long periodStartTimeInManifestUs = Util.msToUs(period.startMs);
long availableEndTimeInManifestUs = Long.MAX_VALUE;
boolean haveAudioVideoAdaptationSets = hasVideoOrAudioAdaptationSets(period);
for (int i = 0; i < period.adaptationSets.size(); i++) {
AdaptationSet adaptationSet = period.adaptationSets.get(i);
List<Representation> representations = adaptationSet.representations;
// or video adaptation set. See: https://github.com/google/ExoPlayer/issues/4029
if ((haveAudioVideoAdaptationSets && adaptationSet.type == C.TRACK_TYPE_TEXT) || representations.isEmpty()) {
continue;
}
@Nullable DashSegmentIndex index = representations.get(0).getIndex();
if (index == null) {
return periodStartTimeInManifestUs + periodDurationUs;
}
long availableSegmentCount = index.getAvailableSegmentCount(periodDurationUs, nowUnixTimeUs);
if (availableSegmentCount == 0) {
return periodStartTimeInManifestUs;
}
long firstAvailableSegmentNum = index.getFirstAvailableSegmentNum(periodDurationUs, nowUnixTimeUs);
long lastAvailableSegmentNum = firstAvailableSegmentNum + availableSegmentCount - 1;
long adaptationSetAvailableEndTimeInManifestUs = periodStartTimeInManifestUs + index.getTimeUs(lastAvailableSegmentNum) + index.getDurationUs(lastAvailableSegmentNum, periodDurationUs);
availableEndTimeInManifestUs = min(availableEndTimeInManifestUs, adaptationSetAvailableEndTimeInManifestUs);
}
return availableEndTimeInManifestUs;
}
use of androidx.media3.exoplayer.dash.manifest.Representation in project media by androidx.
the class DashUtil method loadFormatWithDrmInitData.
/**
* Loads a {@link Format} for acquiring keys for a given period in a DASH manifest.
*
* @param dataSource The {@link HttpDataSource} from which data should be loaded.
* @param period The {@link Period}.
* @return The loaded {@link Format}, or null if none is defined.
* @throws IOException Thrown when there is an error while loading.
*/
@Nullable
public static Format loadFormatWithDrmInitData(DataSource dataSource, Period period) throws IOException {
@C.TrackType int primaryTrackType = C.TRACK_TYPE_VIDEO;
Representation representation = getFirstRepresentation(period, primaryTrackType);
if (representation == null) {
primaryTrackType = C.TRACK_TYPE_AUDIO;
representation = getFirstRepresentation(period, primaryTrackType);
if (representation == null) {
return null;
}
}
Format manifestFormat = representation.format;
@Nullable Format sampleFormat = DashUtil.loadSampleFormat(dataSource, primaryTrackType, representation);
return sampleFormat == null ? manifestFormat : sampleFormat.withManifestFormatInfo(manifestFormat);
}
use of androidx.media3.exoplayer.dash.manifest.Representation in project media by androidx.
the class DashUtil method loadInitializationData.
/**
* Loads initialization data for the {@code representation} and optionally index data then returns
* a {@link BundledChunkExtractor} which contains the output.
*
* @param chunkExtractor The {@link ChunkExtractor} to use.
* @param dataSource The source from which the data should be loaded.
* @param representation The representation which initialization chunk belongs to.
* @param baseUrlIndex The index of the base URL with which to resolve the request URI.
* @param loadIndex Whether to load index data too.
* @throws IOException Thrown when there is an error while loading.
*/
private static void loadInitializationData(ChunkExtractor chunkExtractor, DataSource dataSource, Representation representation, int baseUrlIndex, boolean loadIndex) throws IOException {
RangedUri initializationUri = Assertions.checkNotNull(representation.getInitializationUri());
@Nullable RangedUri requestUri;
if (loadIndex) {
@Nullable RangedUri indexUri = representation.getIndexUri();
if (indexUri == null) {
return;
}
// It's common for initialization and index data to be stored adjacently. Attempt to merge
// the two requests together to request both at once.
requestUri = initializationUri.attemptMerge(indexUri, representation.baseUrls.get(baseUrlIndex).url);
if (requestUri == null) {
loadInitializationData(dataSource, representation, baseUrlIndex, chunkExtractor, initializationUri);
requestUri = indexUri;
}
} else {
requestUri = initializationUri;
}
loadInitializationData(dataSource, representation, baseUrlIndex, chunkExtractor, requestUri);
}
Aggregations