Search in sources :

Example 6 with Chunk

use of com.google.android.exoplayer2.source.chunk.Chunk in project ExoPlayer by google.

the class HlsSampleStreamWrapper method buildTracks.

/**
   * Builds tracks that are exposed by this {@link HlsSampleStreamWrapper} instance, as well as
   * internal data-structures required for operation.
   * <p>
   * Tracks in HLS are complicated. A HLS master playlist contains a number of "variants". Each
   * variant stream typically contains muxed video, audio and (possibly) additional audio, metadata
   * and caption tracks. We wish to allow the user to select between an adaptive track that spans
   * all variants, as well as each individual variant. If multiple audio tracks are present within
   * each variant then we wish to allow the user to select between those also.
   * <p>
   * To do this, tracks are constructed as follows. The {@link HlsChunkSource} exposes (N+1) tracks,
   * where N is the number of variants defined in the HLS master playlist. These consist of one
   * adaptive track defined to span all variants and a track for each individual variant. The
   * adaptive track is initially selected. The extractor is then prepared to discover the tracks
   * inside of each variant stream. The two sets of tracks are then combined by this method to
   * create a third set, which is the set exposed by this {@link HlsSampleStreamWrapper}:
   * <ul>
   * <li>The extractor tracks are inspected to infer a "primary" track type. If a video track is
   * present then it is always the primary type. If not, audio is the primary type if present.
   * Else text is the primary type if present. Else there is no primary type.</li>
   * <li>If there is exactly one extractor track of the primary type, it's expanded into (N+1)
   * exposed tracks, all of which correspond to the primary extractor track and each of which
   * corresponds to a different chunk source track. Selecting one of these tracks has the effect
   * of switching the selected track on the chunk source.</li>
   * <li>All other extractor tracks are exposed directly. Selecting one of these tracks has the
   * effect of selecting an extractor track, leaving the selected track on the chunk source
   * unchanged.</li>
   * </ul>
   */
private void buildTracks() {
    // Iterate through the extractor tracks to discover the "primary" track type, and the index
    // of the single track of this type.
    int primaryExtractorTrackType = PRIMARY_TYPE_NONE;
    int primaryExtractorTrackIndex = C.INDEX_UNSET;
    int extractorTrackCount = sampleQueues.size();
    for (int i = 0; i < extractorTrackCount; i++) {
        String sampleMimeType = sampleQueues.valueAt(i).getUpstreamFormat().sampleMimeType;
        int trackType;
        if (MimeTypes.isVideo(sampleMimeType)) {
            trackType = PRIMARY_TYPE_VIDEO;
        } else if (MimeTypes.isAudio(sampleMimeType)) {
            trackType = PRIMARY_TYPE_AUDIO;
        } else if (MimeTypes.isText(sampleMimeType)) {
            trackType = PRIMARY_TYPE_TEXT;
        } else {
            trackType = PRIMARY_TYPE_NONE;
        }
        if (trackType > primaryExtractorTrackType) {
            primaryExtractorTrackType = trackType;
            primaryExtractorTrackIndex = i;
        } else if (trackType == primaryExtractorTrackType && primaryExtractorTrackIndex != C.INDEX_UNSET) {
            // We have multiple tracks of the primary type. We only want an index if there only exists a
            // single track of the primary type, so unset the index again.
            primaryExtractorTrackIndex = C.INDEX_UNSET;
        }
    }
    TrackGroup chunkSourceTrackGroup = chunkSource.getTrackGroup();
    int chunkSourceTrackCount = chunkSourceTrackGroup.length;
    // Instantiate the necessary internal data-structures.
    primaryTrackGroupIndex = C.INDEX_UNSET;
    groupEnabledStates = new boolean[extractorTrackCount];
    // Construct the set of exposed track groups.
    TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
    for (int i = 0; i < extractorTrackCount; i++) {
        Format sampleFormat = sampleQueues.valueAt(i).getUpstreamFormat();
        if (i == primaryExtractorTrackIndex) {
            Format[] formats = new Format[chunkSourceTrackCount];
            for (int j = 0; j < chunkSourceTrackCount; j++) {
                formats[j] = deriveFormat(chunkSourceTrackGroup.getFormat(j), sampleFormat);
            }
            trackGroups[i] = new TrackGroup(formats);
            primaryTrackGroupIndex = i;
        } else {
            Format trackFormat = primaryExtractorTrackType == PRIMARY_TYPE_VIDEO && MimeTypes.isAudio(sampleFormat.sampleMimeType) ? muxedAudioFormat : null;
            trackGroups[i] = new TrackGroup(deriveFormat(trackFormat, sampleFormat));
        }
    }
    this.trackGroups = new TrackGroupArray(trackGroups);
}
Also used : Format(com.google.android.exoplayer2.Format) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) TrackGroupArray(com.google.android.exoplayer2.source.TrackGroupArray)

Example 7 with Chunk

use of com.google.android.exoplayer2.source.chunk.Chunk in project ExoPlayer by google.

the class HlsSampleStreamWrapper method continueLoading.

// SequenceableLoader implementation
@Override
public boolean continueLoading(long positionUs) {
    if (loadingFinished || loader.isLoading()) {
        return false;
    }
    chunkSource.getNextChunk(mediaChunks.isEmpty() ? null : mediaChunks.getLast(), pendingResetPositionUs != C.TIME_UNSET ? pendingResetPositionUs : positionUs, nextChunkHolder);
    boolean endOfStream = nextChunkHolder.endOfStream;
    Chunk loadable = nextChunkHolder.chunk;
    HlsMasterPlaylist.HlsUrl playlistToLoad = nextChunkHolder.playlist;
    nextChunkHolder.clear();
    if (endOfStream) {
        loadingFinished = true;
        return true;
    }
    if (loadable == null) {
        if (playlistToLoad != null) {
            callback.onPlaylistRefreshRequired(playlistToLoad);
        }
        return false;
    }
    if (isMediaChunk(loadable)) {
        pendingResetPositionUs = C.TIME_UNSET;
        HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable;
        mediaChunk.init(this);
        mediaChunks.add(mediaChunk);
    }
    long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount);
    eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs);
    return true;
}
Also used : Chunk(com.google.android.exoplayer2.source.chunk.Chunk) HlsMasterPlaylist(com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist) HlsUrl(com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl)

Example 8 with Chunk

use of com.google.android.exoplayer2.source.chunk.Chunk in project ExoPlayer by google.

the class TimestampAdjusterProvider method getAdjuster.

/**
   * Returns a {@link TimestampAdjuster} suitable for adjusting the pts timestamps contained in
   * a chunk with a given discontinuity sequence.
   *
   * @param discontinuitySequence The chunk's discontinuity sequence.
   * @return A {@link TimestampAdjuster}.
   */
public TimestampAdjuster getAdjuster(int discontinuitySequence) {
    TimestampAdjuster adjuster = timestampAdjusters.get(discontinuitySequence);
    if (adjuster == null) {
        adjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET);
        timestampAdjusters.put(discontinuitySequence, adjuster);
    }
    return adjuster;
}
Also used : TimestampAdjuster(com.google.android.exoplayer2.util.TimestampAdjuster)

Example 9 with Chunk

use of com.google.android.exoplayer2.source.chunk.Chunk in project ExoPlayer by google.

the class DefaultDashChunkSource method newInitializationChunk.

private static Chunk newInitializationChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) {
    RangedUri requestUri;
    String baseUrl = representationHolder.representation.baseUrl;
    if (initializationUri != null) {
        // 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, baseUrl);
        if (requestUri == null) {
            requestUri = initializationUri;
        }
    } else {
        requestUri = indexUri;
    }
    DataSpec dataSpec = new DataSpec(requestUri.resolveUri(baseUrl), requestUri.start, requestUri.length, representationHolder.representation.getCacheKey());
    return new InitializationChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper);
}
Also used : InitializationChunk(com.google.android.exoplayer2.source.chunk.InitializationChunk) RangedUri(com.google.android.exoplayer2.source.dash.manifest.RangedUri) DataSpec(com.google.android.exoplayer2.upstream.DataSpec)

Example 10 with Chunk

use of com.google.android.exoplayer2.source.chunk.Chunk in project ExoPlayer by google.

the class DefaultDashChunkSource method getNextChunk.

@Override
public final void getNextChunk(MediaChunk previous, long playbackPositionUs, ChunkHolder out) {
    if (fatalError != null) {
        return;
    }
    long bufferedDurationUs = previous != null ? (previous.endTimeUs - playbackPositionUs) : 0;
    trackSelection.updateSelectedTrack(bufferedDurationUs);
    RepresentationHolder representationHolder = representationHolders[trackSelection.getSelectedIndex()];
    Representation selectedRepresentation = representationHolder.representation;
    DashSegmentIndex segmentIndex = representationHolder.segmentIndex;
    RangedUri pendingInitializationUri = null;
    RangedUri pendingIndexUri = null;
    if (representationHolder.extractorWrapper.getSampleFormats() == null) {
        pendingInitializationUri = selectedRepresentation.getInitializationUri();
    }
    if (segmentIndex == null) {
        pendingIndexUri = selectedRepresentation.getIndexUri();
    }
    if (pendingInitializationUri != null || pendingIndexUri != null) {
        // We have initialization and/or index requests to make.
        out.chunk = newInitializationChunk(representationHolder, dataSource, trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(), trackSelection.getSelectionData(), pendingInitializationUri, pendingIndexUri);
        return;
    }
    long nowUs = getNowUnixTimeUs();
    int availableSegmentCount = representationHolder.getSegmentCount();
    if (availableSegmentCount == 0) {
        // The index doesn't define any segments.
        out.endOfStream = !manifest.dynamic || (periodIndex < manifest.getPeriodCount() - 1);
        return;
    }
    int firstAvailableSegmentNum = representationHolder.getFirstSegmentNum();
    int lastAvailableSegmentNum;
    if (availableSegmentCount == DashSegmentIndex.INDEX_UNBOUNDED) {
        // The index is itself unbounded. We need to use the current time to calculate the range of
        // available segments.
        long liveEdgeTimeUs = nowUs - manifest.availabilityStartTime * 1000;
        long periodStartUs = manifest.getPeriod(periodIndex).startMs * 1000;
        long liveEdgeTimeInPeriodUs = liveEdgeTimeUs - periodStartUs;
        if (manifest.timeShiftBufferDepth != C.TIME_UNSET) {
            long bufferDepthUs = manifest.timeShiftBufferDepth * 1000;
            firstAvailableSegmentNum = Math.max(firstAvailableSegmentNum, representationHolder.getSegmentNum(liveEdgeTimeInPeriodUs - bufferDepthUs));
        }
        // getSegmentNum(liveEdgeTimestampUs) will not be completed yet, so subtract one to get the
        // index of the last completed segment.
        lastAvailableSegmentNum = representationHolder.getSegmentNum(liveEdgeTimeInPeriodUs) - 1;
    } else {
        lastAvailableSegmentNum = firstAvailableSegmentNum + availableSegmentCount - 1;
    }
    int segmentNum;
    if (previous == null) {
        segmentNum = Util.constrainValue(representationHolder.getSegmentNum(playbackPositionUs), firstAvailableSegmentNum, lastAvailableSegmentNum);
    } else {
        segmentNum = previous.getNextChunkIndex();
        if (segmentNum < firstAvailableSegmentNum) {
            // This is before the first chunk in the current manifest.
            fatalError = new BehindLiveWindowException();
            return;
        }
    }
    if (segmentNum > lastAvailableSegmentNum || (missingLastSegment && segmentNum >= lastAvailableSegmentNum)) {
        // This is beyond the last chunk in the current manifest.
        out.endOfStream = !manifest.dynamic || (periodIndex < manifest.getPeriodCount() - 1);
        return;
    }
    int maxSegmentCount = Math.min(maxSegmentsPerLoad, lastAvailableSegmentNum - segmentNum + 1);
    out.chunk = newMediaChunk(representationHolder, dataSource, trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(), trackSelection.getSelectionData(), segmentNum, maxSegmentCount);
}
Also used : BehindLiveWindowException(com.google.android.exoplayer2.source.BehindLiveWindowException) RangedUri(com.google.android.exoplayer2.source.dash.manifest.RangedUri) Representation(com.google.android.exoplayer2.source.dash.manifest.Representation)

Aggregations

DataSpec (com.google.android.exoplayer2.upstream.DataSpec)5 RangedUri (com.google.android.exoplayer2.source.dash.manifest.RangedUri)4 ParserException (com.google.android.exoplayer2.ParserException)3 BehindLiveWindowException (com.google.android.exoplayer2.source.BehindLiveWindowException)3 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)3 Uri (android.net.Uri)2 Format (com.google.android.exoplayer2.Format)2 ChunkExtractorWrapper (com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper)2 ContainerMediaChunk (com.google.android.exoplayer2.source.chunk.ContainerMediaChunk)2 InitializationChunk (com.google.android.exoplayer2.source.chunk.InitializationChunk)2 Representation (com.google.android.exoplayer2.source.dash.manifest.Representation)2 HlsUrl (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl)2 TimestampAdjuster (com.google.android.exoplayer2.util.TimestampAdjuster)2 ChunkIndex (com.google.android.exoplayer2.extractor.ChunkIndex)1 DefaultExtractorInput (com.google.android.exoplayer2.extractor.DefaultExtractorInput)1 DefaultTrackOutput (com.google.android.exoplayer2.extractor.DefaultTrackOutput)1 Extractor (com.google.android.exoplayer2.extractor.Extractor)1 ExtractorInput (com.google.android.exoplayer2.extractor.ExtractorInput)1 SeekMap (com.google.android.exoplayer2.extractor.SeekMap)1 Mp3Extractor (com.google.android.exoplayer2.extractor.mp3.Mp3Extractor)1