use of androidx.media3.exoplayer.analytics.PlayerId in project media by androidx.
the class MediaSourceList method prepareChildSource.
private void prepareChildSource(MediaSourceHolder holder) {
MediaSource mediaSource = holder.mediaSource;
MediaSource.MediaSourceCaller caller = (source, timeline) -> mediaSourceListInfoListener.onPlaylistUpdateRequested();
ForwardingEventListener eventListener = new ForwardingEventListener(holder);
childSources.put(holder, new MediaSourceAndListener(mediaSource, caller, eventListener));
mediaSource.addEventListener(Util.createHandlerForCurrentOrMainLooper(), eventListener);
mediaSource.addDrmEventListener(Util.createHandlerForCurrentOrMainLooper(), eventListener);
mediaSource.prepareSource(caller, mediaTransferListener, playerId);
}
use of androidx.media3.exoplayer.analytics.PlayerId in project media by androidx.
the class DashMediaPeriod method buildSampleStream.
private ChunkSampleStream<DashChunkSource> buildSampleStream(TrackGroupInfo trackGroupInfo, ExoTrackSelection selection, long positionUs) {
int embeddedTrackCount = 0;
boolean enableEventMessageTrack = trackGroupInfo.embeddedEventMessageTrackGroupIndex != C.INDEX_UNSET;
TrackGroup embeddedEventMessageTrackGroup = null;
if (enableEventMessageTrack) {
embeddedEventMessageTrackGroup = trackGroups.get(trackGroupInfo.embeddedEventMessageTrackGroupIndex);
embeddedTrackCount++;
}
boolean enableClosedCaptionTrack = trackGroupInfo.embeddedClosedCaptionTrackGroupIndex != C.INDEX_UNSET;
TrackGroup embeddedClosedCaptionTrackGroup = null;
if (enableClosedCaptionTrack) {
embeddedClosedCaptionTrackGroup = trackGroups.get(trackGroupInfo.embeddedClosedCaptionTrackGroupIndex);
embeddedTrackCount += embeddedClosedCaptionTrackGroup.length;
}
Format[] embeddedTrackFormats = new Format[embeddedTrackCount];
int[] embeddedTrackTypes = new int[embeddedTrackCount];
embeddedTrackCount = 0;
if (enableEventMessageTrack) {
embeddedTrackFormats[embeddedTrackCount] = embeddedEventMessageTrackGroup.getFormat(0);
embeddedTrackTypes[embeddedTrackCount] = C.TRACK_TYPE_METADATA;
embeddedTrackCount++;
}
List<Format> embeddedClosedCaptionTrackFormats = new ArrayList<>();
if (enableClosedCaptionTrack) {
for (int i = 0; i < embeddedClosedCaptionTrackGroup.length; i++) {
embeddedTrackFormats[embeddedTrackCount] = embeddedClosedCaptionTrackGroup.getFormat(i);
embeddedTrackTypes[embeddedTrackCount] = C.TRACK_TYPE_TEXT;
embeddedClosedCaptionTrackFormats.add(embeddedTrackFormats[embeddedTrackCount]);
embeddedTrackCount++;
}
}
PlayerTrackEmsgHandler trackPlayerEmsgHandler = manifest.dynamic && enableEventMessageTrack ? playerEmsgHandler.newPlayerTrackEmsgHandler() : null;
DashChunkSource chunkSource = chunkSourceFactory.createDashChunkSource(manifestLoaderErrorThrower, manifest, baseUrlExclusionList, periodIndex, trackGroupInfo.adaptationSetIndices, selection, trackGroupInfo.trackType, elapsedRealtimeOffsetMs, enableEventMessageTrack, embeddedClosedCaptionTrackFormats, trackPlayerEmsgHandler, transferListener, playerId);
ChunkSampleStream<DashChunkSource> stream = new ChunkSampleStream<>(trackGroupInfo.trackType, embeddedTrackTypes, embeddedTrackFormats, chunkSource, this, allocator, positionUs, drmSessionManager, drmEventDispatcher, loadErrorHandlingPolicy, mediaSourceEventDispatcher);
synchronized (this) {
// The map is also accessed on the loading thread so synchronize access.
trackEmsgHandlerBySampleStream.put(stream, trackPlayerEmsgHandler);
}
return stream;
}
use of androidx.media3.exoplayer.analytics.PlayerId in project media by androidx.
the class HlsChunkSource method getNextChunk.
/**
* Returns the next chunk to load.
*
* <p>If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream
* has been reached then {@link HlsChunkHolder#endOfStream} is set. If a chunk is not available
* but the end of the stream has not been reached, {@link HlsChunkHolder#playlistUrl} is set to
* contain the {@link Uri} that refers to the playlist that needs refreshing.
*
* @param playbackPositionUs The current playback position relative to the period start in
* microseconds. If playback of the period to which this chunk source belongs has not yet
* started, the value will be the starting position in the period minus the duration of any
* media in previous periods still to be played.
* @param loadPositionUs The current load position relative to the period start in microseconds.
* @param queue The queue of buffered {@link HlsMediaChunk}s.
* @param allowEndOfStream Whether {@link HlsChunkHolder#endOfStream} is allowed to be set for
* non-empty media playlists. If {@code false}, the last available chunk is returned instead.
* If the media playlist is empty, {@link HlsChunkHolder#endOfStream} is always set.
* @param out A holder to populate.
*/
public void getNextChunk(long playbackPositionUs, long loadPositionUs, List<HlsMediaChunk> queue, boolean allowEndOfStream, HlsChunkHolder out) {
@Nullable HlsMediaChunk previous = queue.isEmpty() ? null : Iterables.getLast(queue);
int oldTrackIndex = previous == null ? C.INDEX_UNSET : trackGroup.indexOf(previous.trackFormat);
long bufferedDurationUs = loadPositionUs - playbackPositionUs;
long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs);
if (previous != null && !independentSegments) {
// Unless segments are known to be independent, switching tracks requires downloading
// overlapping segments. Hence we subtract the previous segment's duration from the buffered
// duration.
// This may affect the live-streaming adaptive track selection logic, when we compare the
// buffered duration to time-to-live-edge to decide whether to switch. Therefore, we subtract
// the duration of the last loaded segment from timeToLiveEdgeUs as well.
long subtractedDurationUs = previous.getDurationUs();
bufferedDurationUs = max(0, bufferedDurationUs - subtractedDurationUs);
if (timeToLiveEdgeUs != C.TIME_UNSET) {
timeToLiveEdgeUs = max(0, timeToLiveEdgeUs - subtractedDurationUs);
}
}
// Select the track.
MediaChunkIterator[] mediaChunkIterators = createMediaChunkIterators(previous, loadPositionUs);
trackSelection.updateSelectedTrack(playbackPositionUs, bufferedDurationUs, timeToLiveEdgeUs, queue, mediaChunkIterators);
int selectedTrackIndex = trackSelection.getSelectedIndexInTrackGroup();
boolean switchingTrack = oldTrackIndex != selectedTrackIndex;
Uri selectedPlaylistUrl = playlistUrls[selectedTrackIndex];
if (!playlistTracker.isSnapshotValid(selectedPlaylistUrl)) {
out.playlistUrl = selectedPlaylistUrl;
seenExpectedPlaylistError &= selectedPlaylistUrl.equals(expectedPlaylistUrl);
expectedPlaylistUrl = selectedPlaylistUrl;
// Retry when playlist is refreshed.
return;
}
@Nullable HlsMediaPlaylist playlist = playlistTracker.getPlaylistSnapshot(selectedPlaylistUrl, /* isForPlayback= */
true);
// playlistTracker snapshot is valid (checked by if() above), so playlist must be non-null.
checkNotNull(playlist);
independentSegments = playlist.hasIndependentSegments;
updateLiveEdgeTimeUs(playlist);
// Select the chunk.
long startOfPlaylistInPeriodUs = playlist.startTimeUs - playlistTracker.getInitialStartTimeUs();
Pair<Long, Integer> nextMediaSequenceAndPartIndex = getNextMediaSequenceAndPartIndex(previous, switchingTrack, playlist, startOfPlaylistInPeriodUs, loadPositionUs);
long chunkMediaSequence = nextMediaSequenceAndPartIndex.first;
int partIndex = nextMediaSequenceAndPartIndex.second;
if (chunkMediaSequence < playlist.mediaSequence && previous != null && switchingTrack) {
// We try getting the next chunk without adapting in case that's the reason for falling
// behind the live window.
selectedTrackIndex = oldTrackIndex;
selectedPlaylistUrl = playlistUrls[selectedTrackIndex];
playlist = playlistTracker.getPlaylistSnapshot(selectedPlaylistUrl, /* isForPlayback= */
true);
// playlistTracker snapshot is valid (checked by if() above), so playlist must be non-null.
checkNotNull(playlist);
startOfPlaylistInPeriodUs = playlist.startTimeUs - playlistTracker.getInitialStartTimeUs();
// Get the next segment/part without switching tracks.
Pair<Long, Integer> nextMediaSequenceAndPartIndexWithoutAdapting = getNextMediaSequenceAndPartIndex(previous, /* switchingTrack= */
false, playlist, startOfPlaylistInPeriodUs, loadPositionUs);
chunkMediaSequence = nextMediaSequenceAndPartIndexWithoutAdapting.first;
partIndex = nextMediaSequenceAndPartIndexWithoutAdapting.second;
}
if (chunkMediaSequence < playlist.mediaSequence) {
fatalError = new BehindLiveWindowException();
return;
}
@Nullable SegmentBaseHolder segmentBaseHolder = getNextSegmentHolder(playlist, chunkMediaSequence, partIndex);
if (segmentBaseHolder == null) {
if (!playlist.hasEndTag) {
// Reload the playlist in case of a live stream.
out.playlistUrl = selectedPlaylistUrl;
seenExpectedPlaylistError &= selectedPlaylistUrl.equals(expectedPlaylistUrl);
expectedPlaylistUrl = selectedPlaylistUrl;
return;
} else if (allowEndOfStream || playlist.segments.isEmpty()) {
out.endOfStream = true;
return;
}
// Use the last segment available in case of a VOD stream.
segmentBaseHolder = new SegmentBaseHolder(Iterables.getLast(playlist.segments), playlist.mediaSequence + playlist.segments.size() - 1, /* partIndex= */
C.INDEX_UNSET);
}
// We have a valid media segment, we can discard any playlist errors at this point.
seenExpectedPlaylistError = false;
expectedPlaylistUrl = null;
// Check if the media segment or its initialization segment are fully encrypted.
@Nullable Uri initSegmentKeyUri = getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase.initializationSegment);
out.chunk = maybeCreateEncryptionChunkFor(initSegmentKeyUri, selectedTrackIndex);
if (out.chunk != null) {
return;
}
@Nullable Uri mediaSegmentKeyUri = getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase);
out.chunk = maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex);
if (out.chunk != null) {
return;
}
boolean shouldSpliceIn = HlsMediaChunk.shouldSpliceIn(previous, selectedPlaylistUrl, playlist, segmentBaseHolder, startOfPlaylistInPeriodUs);
if (shouldSpliceIn && segmentBaseHolder.isPreload) {
// becomes fully available (or the track selection selects another track).
return;
}
out.chunk = HlsMediaChunk.createInstance(extractorFactory, mediaDataSource, playlistFormats[selectedTrackIndex], startOfPlaylistInPeriodUs, playlist, segmentBaseHolder, selectedPlaylistUrl, muxedCaptionFormats, trackSelection.getSelectionReason(), trackSelection.getSelectionData(), isTimestampMaster, timestampAdjusterProvider, previous, /* mediaSegmentKey= */
keyCache.get(mediaSegmentKeyUri), /* initSegmentKey= */
keyCache.get(initSegmentKeyUri), shouldSpliceIn, playerId);
}
Aggregations