/** * @param manifestLoaderErrorThrower Throws errors affecting loading of manifests. * @param manifest The initial manifest. * @param periodIndex The index of the period in the manifest. * @param adaptationSetIndex The index of the adaptation set in the period. * @param trackSelection The track selection. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between * server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified * as the server's unix time minus the local elapsed time. If unknown, set to 0. * @param maxSegmentsPerLoad The maximum number of segments to combine into a single request. * Note that segments will only be combined if their {@link Uri}s are the same and if their * data ranges are adjacent. * @param enableEventMessageTrack Whether the chunks generated by the source may output an event * message track. * @param enableEventMessageTrack Whether the chunks generated by the source may output a CEA-608 * track. */ public DefaultDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, DashManifest manifest, int periodIndex, int adaptationSetIndex, TrackSelection trackSelection, DataSource dataSource, long elapsedRealtimeOffsetMs, int maxSegmentsPerLoad, boolean enableEventMessageTrack, boolean enableCea608Track) { this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifest = manifest; this.adaptationSetIndex = adaptationSetIndex; this.trackSelection = trackSelection; this.dataSource = dataSource; this.periodIndex = periodIndex; this.elapsedRealtimeOffsetMs = elapsedRealtimeOffsetMs; this.maxSegmentsPerLoad = maxSegmentsPerLoad; long periodDurationUs = manifest.getPeriodDurationUs(periodIndex); AdaptationSet adaptationSet = getAdaptationSet(); List<Representation> representations = adaptationSet.representations; representationHolders = new RepresentationHolder[trackSelection.length()]; for (int i = 0; i < representationHolders.length; i++) { Representation representation = representations.get(trackSelection.getIndexInTrackGroup(i)); representationHolders[i] = new RepresentationHolder(periodDurationUs, representation, enableEventMessageTrack, enableCea608Track, adaptationSet.type); } }
public DashMediaPeriod(int id, DashManifest manifest, int periodIndex, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, long elapsedRealtimeOffset, LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { this.id = id; this.manifest = manifest; this.periodIndex = periodIndex; this.chunkSourceFactory = chunkSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.elapsedRealtimeOffset = elapsedRealtimeOffset; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.allocator = allocator; sampleStreams = newSampleStreamArray(0); sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); adaptationSets = manifest.getPeriod(periodIndex).adaptationSets; Pair<TrackGroupArray, EmbeddedTrackInfo[]> result = buildTrackGroups(adaptationSets); trackGroups = result.first; embeddedTrackInfos = result.second; }
/** * @param manifestLoaderErrorThrower Throws errors affecting loading of manifests. * @param manifest The initial manifest. * @param periodIndex The index of the period in the manifest. * @param adaptationSetIndex The index of the adaptation set in the period. * @param trackSelection The track selection. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between * server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified * as the server's unix time minus the local elapsed time. If unknown, set to 0. */ public DefaultDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, DashManifest manifest, int periodIndex, int adaptationSetIndex, TrackSelection trackSelection, DataSource dataSource, long elapsedRealtimeOffsetMs) { this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifest = manifest; this.adaptationSetIndex = adaptationSetIndex; this.trackSelection = trackSelection; this.dataSource = dataSource; this.periodIndex = periodIndex; this.elapsedRealtimeOffsetMs = elapsedRealtimeOffsetMs; long periodDurationUs = manifest.getPeriodDurationUs(periodIndex); List<Representation> representations = getRepresentations(); representationHolders = new RepresentationHolder[trackSelection.length()]; for (int i = 0; i < representationHolders.length; i++) { Representation representation = representations.get(trackSelection.getIndexInTrackGroup(i)); representationHolders[i] = new RepresentationHolder(periodDurationUs, representation); } }
public DashMediaPeriod(int id, DashManifest manifest, int index, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, long elapsedRealtimeOffset, LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { this.id = id; this.manifest = manifest; this.index = index; this.chunkSourceFactory = chunkSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.elapsedRealtimeOffset = elapsedRealtimeOffset; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.allocator = allocator; sampleStreams = newSampleStreamArray(0); sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); period = manifest.getPeriod(index); trackGroups = buildTrackGroups(period); }
/** * @param manifestLoaderErrorThrower Throws errors affecting loading of manifests. * @param manifest The initial manifest. * @param periodIndex The index of the period in the manifest. * @param adaptationSetIndices The indices of the adaptation sets in the period. * @param trackSelection The track selection. * @param trackType The type of the tracks in the selection. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between * server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified * as the server's unix time minus the local elapsed time. If unknown, set to 0. * @param maxSegmentsPerLoad The maximum number of segments to combine into a single request. * Note that segments will only be combined if their {@link Uri}s are the same and if their * data ranges are adjacent. * @param enableEventMessageTrack Whether the chunks generated by the source may output an event * message track. * @param enableCea608Track Whether the chunks generated by the source may output a CEA-608 track. */ public DefaultDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, DashManifest manifest, int periodIndex, int[] adaptationSetIndices, TrackSelection trackSelection, int trackType, DataSource dataSource, long elapsedRealtimeOffsetMs, int maxSegmentsPerLoad, boolean enableEventMessageTrack, boolean enableCea608Track) { this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifest = manifest; this.adaptationSetIndices = adaptationSetIndices; this.trackSelection = trackSelection; this.trackType = trackType; this.dataSource = dataSource; this.periodIndex = periodIndex; this.elapsedRealtimeOffsetMs = elapsedRealtimeOffsetMs; this.maxSegmentsPerLoad = maxSegmentsPerLoad; long periodDurationUs = manifest.getPeriodDurationUs(periodIndex); liveEdgeTimeUs = C.TIME_UNSET; List<Representation> representations = getRepresentations(); representationHolders = new RepresentationHolder[trackSelection.length()]; for (int i = 0; i < representationHolders.length; i++) { Representation representation = representations.get(trackSelection.getIndexInTrackGroup(i)); representationHolders[i] = new RepresentationHolder(periodDurationUs, trackType, representation, enableEventMessageTrack, enableCea608Track); } }
/** * Returns a new {@link DashMediaSource} using the current parameters and the specified * sideloaded manifest. * * @param manifest The manifest. {@link DashManifest#dynamic} must be false. * @param eventHandler A handler for events. * @param eventListener A listener of events. * @return The new {@link DashMediaSource}. * @throws IllegalArgumentException If {@link DashManifest#dynamic} is true. */ public DashMediaSource createMediaSource( DashManifest manifest, @Nullable Handler eventHandler, @Nullable MediaSourceEventListener eventListener) { Assertions.checkArgument(!manifest.dynamic); isCreateCalled = true; return new DashMediaSource( manifest, null, null, null, chunkSourceFactory, minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener); }
@Override protected List<Segment> getAllSegments(DataSource dataSource, DashManifest manifest, boolean allowIndexLoadErrors) throws InterruptedException, IOException { ArrayList<Segment> segments = new ArrayList<>(); for (int periodIndex = 0; periodIndex < manifest.getPeriodCount(); periodIndex++) { List<AdaptationSet> adaptationSets = manifest.getPeriod(periodIndex).adaptationSets; for (int adaptationIndex = 0; adaptationIndex < adaptationSets.size(); adaptationIndex++) { AdaptationSet adaptationSet = adaptationSets.get(adaptationIndex); RepresentationKey[] keys = new RepresentationKey[adaptationSet.representations.size()]; for (int i = 0; i < keys.length; i++) { keys[i] = new RepresentationKey(periodIndex, adaptationIndex, i); } segments.addAll(getSegments(dataSource, manifest, keys, allowIndexLoadErrors)); } } return segments; }
public DashMediaPeriod(int id, DashManifest manifest, int periodIndex, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, long elapsedRealtimeOffset, LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { this.id = id; this.manifest = manifest; this.periodIndex = periodIndex; this.chunkSourceFactory = chunkSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.elapsedRealtimeOffset = elapsedRealtimeOffset; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.allocator = allocator; sampleStreams = newSampleStreamArray(0); sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); Pair<TrackGroupArray, TrackGroupInfo[]> result = buildTrackGroups(manifest.getPeriod(periodIndex).adaptationSets); trackGroups = result.first; trackGroupInfos = result.second; }
public void testDownloadManifestFailure() throws Exception { byte[] testMpdFirstPart = Arrays.copyOf(TEST_MPD, 10); byte[] testMpdSecondPart = Arrays.copyOfRange(TEST_MPD, 10, TEST_MPD.length); FakeDataSet fakeDataSet = new FakeDataSet() .newData(TEST_MPD_URI) .appendReadData(testMpdFirstPart) .appendReadError(new IOException()) .appendReadData(testMpdSecondPart) .endData(); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); // fails on the first try try { dashDownloader.getManifest(); fail(); } catch (IOException e) { // ignore } assertDataCached(cache, TEST_MPD_URI, testMpdFirstPart); // on the second try it downloads the rest of the data DashManifest manifest = dashDownloader.getManifest(); assertNotNull(manifest); assertCachedData(cache, fakeDataSet); }
/** * Downloads an offline license. * * @param dataSource The {@link HttpDataSource} to be used for download. * @param dashManifest The {@link DashManifest} of the DASH content. * @return The downloaded offline license key set id. * @throws IOException If an error occurs reading data from the stream. * @throws InterruptedException If the thread has been interrupted. * @throws DrmSessionException Thrown when there is an error during DRM session. */ public byte[] download(HttpDataSource dataSource, DashManifest dashManifest) throws IOException, InterruptedException, DrmSessionException { // Get DrmInitData // Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream, // as per DASH IF Interoperability Recommendations V3.0, 7.5.3. if (dashManifest.getPeriodCount() < 1) { return null; } Period period = dashManifest.getPeriod(0); int adaptationSetIndex = period.getAdaptationSetIndex(C.TRACK_TYPE_VIDEO); if (adaptationSetIndex == C.INDEX_UNSET) { adaptationSetIndex = period.getAdaptationSetIndex(C.TRACK_TYPE_AUDIO); if (adaptationSetIndex == C.INDEX_UNSET) { return null; } } AdaptationSet adaptationSet = period.adaptationSets.get(adaptationSetIndex); if (adaptationSet.representations.isEmpty()) { return null; } Representation representation = adaptationSet.representations.get(0); DrmInitData drmInitData = representation.format.drmInitData; if (drmInitData == null) { Format sampleFormat = DashUtil.loadSampleFormat(dataSource, representation); if (sampleFormat != null) { drmInitData = sampleFormat.drmInitData; } if (drmInitData == null) { return null; } } blockingKeyRequest(DefaultDrmSessionManager.MODE_DOWNLOAD, null, drmInitData); return drmSessionManager.getOfflineLicenseKeySetId(); }
@Override public DashChunkSource createDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, DashManifest manifest, int periodIndex, int adaptationSetIndex, TrackSelection trackSelection, long elapsedRealtimeOffsetMs, boolean enableEventMessageTrack, boolean enableCea608Track) { DataSource dataSource = dataSourceFactory.createDataSource(); return new DefaultDashChunkSource(manifestLoaderErrorThrower, manifest, periodIndex, adaptationSetIndex, trackSelection, dataSource, elapsedRealtimeOffsetMs, maxSegmentsPerLoad, enableEventMessageTrack, enableCea608Track); }
@Override public void updateManifest(DashManifest newManifest, int newPeriodIndex) { try { manifest = newManifest; periodIndex = newPeriodIndex; long periodDurationUs = manifest.getPeriodDurationUs(periodIndex); List<Representation> representations = getAdaptationSet().representations; for (int i = 0; i < representationHolders.length; i++) { Representation representation = representations.get(trackSelection.getIndexInTrackGroup(i)); representationHolders[i].updateRepresentation(periodDurationUs, representation); } } catch (BehindLiveWindowException e) { fatalError = e; } }
/** * Loads a DASH manifest. * * @param dataSource The {@link HttpDataSource} from which the manifest should be read. * @param manifestUriString The URI of the manifest to be read. * @return An instance of {@link DashManifest}. * @throws IOException If an error occurs reading data from the stream. * @see DashManifestParser */ public static DashManifest loadManifest(DataSource dataSource, String manifestUriString) throws IOException { DataSourceInputStream inputStream = new DataSourceInputStream(dataSource, new DataSpec(Uri.parse(manifestUriString), DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH)); try { inputStream.open(); DashManifestParser parser = new DashManifestParser(); return parser.parse(dataSource.getUri(), inputStream); } finally { inputStream.close(); } }
private DashMediaSource(DashManifest manifest, Uri manifestUri, DataSource.Factory manifestDataSourceFactory, DashManifestParser manifestParser, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, long livePresentationDelayMs, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { this.manifest = manifest; this.manifestUri = manifestUri; this.manifestDataSourceFactory = manifestDataSourceFactory; this.manifestParser = manifestParser; this.chunkSourceFactory = chunkSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.livePresentationDelayMs = livePresentationDelayMs; sideloadedManifest = manifest != null; eventDispatcher = new EventDispatcher(eventHandler, eventListener); manifestUriLock = new Object(); periodsById = new SparseArray<>(); if (sideloadedManifest) { Assertions.checkState(!manifest.dynamic); manifestCallback = null; refreshManifestRunnable = null; simulateManifestRefreshRunnable = null; } else { manifestCallback = new ManifestCallback(); refreshManifestRunnable = new Runnable() { @Override public void run() { startLoadingManifest(); } }; simulateManifestRefreshRunnable = new Runnable() { @Override public void run() { processManifest(false); } }; } }
int onManifestLoadError(ParsingLoadable<DashManifest> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { boolean isFatal = error instanceof ParserException; eventDispatcher.loadError(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, isFatal); return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY; }
public DashTimeline(long presentationStartTimeMs, long windowStartTimeMs, int firstPeriodId, long offsetInFirstPeriodUs, long windowDurationUs, long windowDefaultStartPositionUs, DashManifest manifest) { this.presentationStartTimeMs = presentationStartTimeMs; this.windowStartTimeMs = windowStartTimeMs; this.firstPeriodId = firstPeriodId; this.offsetInFirstPeriodUs = offsetInFirstPeriodUs; this.windowDurationUs = windowDurationUs; this.windowDefaultStartPositionUs = windowDefaultStartPositionUs; this.manifest = manifest; }
public void updateManifest(DashManifest manifest, int periodIndex) { this.manifest = manifest; this.periodIndex = periodIndex; adaptationSets = manifest.getPeriod(periodIndex).adaptationSets; if (sampleStreams != null) { for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) { sampleStream.getChunkSource().updateManifest(manifest, periodIndex); } callback.onContinueLoadingRequested(this); } }
@Override public DashChunkSource createDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, DashManifest manifest, int periodIndex, int adaptationSetIndex, TrackSelection trackSelection, long elapsedRealtimeOffsetMs) { DataSource dataSource = dataSourceFactory.createDataSource(); return new DefaultDashChunkSource(manifestLoaderErrorThrower, manifest, periodIndex, adaptationSetIndex, trackSelection, dataSource, elapsedRealtimeOffsetMs); }
@Override public void updateManifest(DashManifest newManifest, int newPeriodIndex) { try { manifest = newManifest; periodIndex = newPeriodIndex; long periodDurationUs = manifest.getPeriodDurationUs(periodIndex); List<Representation> representations = getRepresentations(); for (int i = 0; i < representationHolders.length; i++) { Representation representation = representations.get(trackSelection.getIndexInTrackGroup(i)); representationHolders[i].updateRepresentation(periodDurationUs, representation); } } catch (BehindLiveWindowException e) { fatalError = e; } }
public void updateManifest(DashManifest manifest, int index) { this.manifest = manifest; this.index = index; period = manifest.getPeriod(index); if (sampleStreams != null) { for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) { sampleStream.getChunkSource().updateManifest(manifest, index); } callback.onContinueLoadingRequested(this); } }
private void downloadLicense() throws InterruptedException, DrmSessionException, IOException { DataSource dataSource = httpDataSourceFactory.createDataSource(); DashManifest dashManifest = DashUtil.loadManifest(dataSource, Uri.parse(DashTestData.WIDEVINE_H264_MANIFEST)); DrmInitData drmInitData = DashUtil.loadDrmInitData(dataSource, dashManifest.getPeriod(0)); offlineLicenseKeySetId = offlineLicenseHelper.downloadLicense(drmInitData); Assert.assertNotNull(offlineLicenseKeySetId); Assert.assertTrue(offlineLicenseKeySetId.length > 0); testRunner.setOfflineLicenseKeySetId(offlineLicenseKeySetId); }
@Override public DashChunkSource createDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, DashManifest manifest, int periodIndex, int[] adaptationSetIndices, TrackSelection trackSelection, int trackType, long elapsedRealtimeOffsetMs, boolean enableEventMessageTrack, boolean enableCea608Track) { DataSource dataSource = dataSourceFactory.createDataSource(); return new DefaultDashChunkSource(manifestLoaderErrorThrower, manifest, periodIndex, adaptationSetIndices, trackSelection, trackType, dataSource, elapsedRealtimeOffsetMs, maxSegmentsPerLoad, enableEventMessageTrack, enableCea608Track); }
/** * Loads a DASH manifest. * * @param dataSource The {@link HttpDataSource} from which the manifest should be read. * @param uri The {@link Uri} of the manifest to be read. * @return An instance of {@link DashManifest}. * @throws IOException Thrown when there is an error while loading. */ public static DashManifest loadManifest(DataSource dataSource, Uri uri) throws IOException { DataSpec dataSpec = new DataSpec(uri, DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH | DataSpec.FLAG_ALLOW_GZIP); ParsingLoadable<DashManifest> loadable = new ParsingLoadable<>(dataSource, dataSpec, C.DATA_TYPE_MANIFEST, new DashManifestParser()); loadable.load(); return loadable.getResult(); }
private DashMediaSource(DashManifest manifest, Uri manifestUri, DataSource.Factory manifestDataSourceFactory, ParsingLoadable.Parser<? extends DashManifest> manifestParser, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, long livePresentationDelayMs, Handler eventHandler, MediaSourceEventListener eventListener) { this.manifest = manifest; this.manifestUri = manifestUri; this.manifestDataSourceFactory = manifestDataSourceFactory; this.manifestParser = manifestParser; this.chunkSourceFactory = chunkSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.livePresentationDelayMs = livePresentationDelayMs; sideloadedManifest = manifest != null; eventDispatcher = new EventDispatcher(eventHandler, eventListener); manifestUriLock = new Object(); periodsById = new SparseArray<>(); if (sideloadedManifest) { Assertions.checkState(!manifest.dynamic); manifestCallback = null; refreshManifestRunnable = null; simulateManifestRefreshRunnable = null; } else { manifestCallback = new ManifestCallback(); refreshManifestRunnable = new Runnable() { @Override public void run() { startLoadingManifest(); } }; simulateManifestRefreshRunnable = new Runnable() { @Override public void run() { processManifest(false); } }; } }
/** * Returns DashSegmentIndex for given representation. */ private DashSegmentIndex getSegmentIndex(DataSource dataSource, DashManifest manifest, RepresentationKey key) throws IOException, InterruptedException { AdaptationSet adaptationSet = manifest.getPeriod(key.periodIndex).adaptationSets.get( key.adaptationSetIndex); Representation representation = adaptationSet.representations.get(key.representationIndex); DashSegmentIndex index = representation.getIndex(); if (index != null) { return index; } ChunkIndex seekMap = DashUtil.loadChunkIndex(dataSource, adaptationSet.type, representation); return seekMap == null ? null : new DashWrappingSegmentIndex(seekMap); }
public void updateManifest(DashManifest manifest, int periodIndex) { this.manifest = manifest; this.periodIndex = periodIndex; if (sampleStreams != null) { for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) { sampleStream.getChunkSource().updateManifest(manifest, periodIndex); } callback.onContinueLoadingRequested(this); } }
public void testGetManifest() throws Exception { FakeDataSet fakeDataSet = new FakeDataSet() .setData(TEST_MPD_URI, TEST_MPD); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); DashManifest manifest = dashDownloader.getManifest(); assertNotNull(manifest); assertCachedData(cache, fakeDataSet); }
void onManifestLoadCompleted(ParsingLoadable<DashManifest> loadable, long elapsedRealtimeMs, long loadDurationMs) { eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded()); DashManifest newManifest = loadable.getResult(); int periodCount = manifest == null ? 0 : manifest.getPeriodCount(); int removedPeriodCount = 0; long newFirstPeriodStartTimeMs = newManifest.getPeriod(0).startMs; while (removedPeriodCount < periodCount && manifest.getPeriod(removedPeriodCount).startMs < newFirstPeriodStartTimeMs) { removedPeriodCount++; } // After discarding old periods, we should never have more periods than listed in the new // manifest. That would mean that a previously announced period is no longer advertised. If // this condition occurs, assume that we are hitting a manifest server that is out of sync and // behind, discard this manifest, and try again later. if (periodCount - removedPeriodCount > newManifest.getPeriodCount()) { Log.w(TAG, "Out of sync manifest"); scheduleManifestRefresh(); return; } manifest = newManifest; manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs; manifestLoadEndTimestamp = elapsedRealtimeMs; if (manifest.location != null) { synchronized (manifestUriLock) { // This condition checks that replaceManifestUri wasn't called between the start and end of // this load. If it was, we ignore the manifest location and prefer the manual replacement. if (loadable.dataSpec.uri == manifestUri) { manifestUri = manifest.location; } } } if (periodCount == 0) { if (manifest.utcTiming != null) { resolveUtcTimingElement(manifest.utcTiming); } else { processManifest(true); } } else { firstPeriodId += removedPeriodCount; processManifest(true); } }
@Override public void onLoadCompleted(ParsingLoadable<DashManifest> loadable, long elapsedRealtimeMs, long loadDurationMs) { onManifestLoadCompleted(loadable, elapsedRealtimeMs, loadDurationMs); }
@Override public void onLoadCanceled(ParsingLoadable<DashManifest> loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) { DashMediaSource.this.onLoadCanceled(loadable, elapsedRealtimeMs, loadDurationMs); }
@Override public int onLoadError(ParsingLoadable<DashManifest> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { return onManifestLoadError(loadable, elapsedRealtimeMs, loadDurationMs, error); }
DashChunkSource createDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, DashManifest manifest, int periodIndex, int adaptationSetIndex, TrackSelection trackSelection, long elapsedRealtimeOffsetMs, boolean enableEventMessageTrack, boolean enableCea608Track);
void onManifestLoadCompleted(ParsingLoadable<DashManifest> loadable, long elapsedRealtimeMs, long loadDurationMs) { eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded()); DashManifest newManifest = loadable.getResult(); int periodCount = manifest == null ? 0 : manifest.getPeriodCount(); int removedPeriodCount = 0; long newFirstPeriodStartTimeMs = newManifest.getPeriod(0).startMs; while (removedPeriodCount < periodCount && manifest.getPeriod(removedPeriodCount).startMs < newFirstPeriodStartTimeMs) { removedPeriodCount++; } // After discarding old periods, we should never have more periods than listed in the new // manifest. That would mean that a previously announced period is no longer advertised. If // this condition occurs, assume that we are hitting a manifest server that is out of sync and // behind, discard this manifest, and try again later. if (periodCount - removedPeriodCount > newManifest.getPeriodCount()) { Log.w(TAG, "Out of sync manifest"); scheduleManifestRefresh(); return; } manifest = newManifest; manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs; manifestLoadEndTimestamp = elapsedRealtimeMs; if (manifest.location != null) { synchronized (manifestUriLock) { // This condition checks that replaceManifestUri wasn't called between the start and end of // this load. If it was, we ignore the manifest location and prefer the manual replacement. if (loadable.dataSpec.uri == manifestUri) { manifestUri = manifest.location; } } } if (periodCount == 0) { if (manifest.utcTiming != null) { resolveUtcTimingElement(manifest.utcTiming); } else { processManifestAndScheduleRefresh(); } } else { firstPeriodId += removedPeriodCount; processManifestAndScheduleRefresh(); } }