Search in sources :

Example 6 with Segment

use of com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment 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)

Example 7 with Segment

use of com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment in project ExoPlayer by google.

the class DefaultDashChunkSource method newMediaChunk.

private static Chunk newMediaChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, int firstSegmentNum, int maxSegmentCount) {
    Representation representation = representationHolder.representation;
    long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
    RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
    String baseUrl = representation.baseUrl;
    if (representationHolder.extractorWrapper == null) {
        long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
        DataSpec dataSpec = new DataSpec(segmentUri.resolveUri(baseUrl), segmentUri.start, segmentUri.length, representation.getCacheKey());
        return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, firstSegmentNum, representationHolder.trackType, trackFormat);
    } else {
        int segmentCount = 1;
        for (int i = 1; i < maxSegmentCount; i++) {
            RangedUri nextSegmentUri = representationHolder.getSegmentUrl(firstSegmentNum + i);
            RangedUri mergedSegmentUri = segmentUri.attemptMerge(nextSegmentUri, baseUrl);
            if (mergedSegmentUri == null) {
                // Unable to merge segment fetches because the URIs do not merge.
                break;
            }
            segmentUri = mergedSegmentUri;
            segmentCount++;
        }
        long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum + segmentCount - 1);
        DataSpec dataSpec = new DataSpec(segmentUri.resolveUri(baseUrl), segmentUri.start, segmentUri.length, representation.getCacheKey());
        long sampleOffsetUs = -representation.presentationTimeOffsetUs;
        return new ContainerMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, firstSegmentNum, segmentCount, sampleOffsetUs, representationHolder.extractorWrapper);
    }
}
Also used : SingleSampleMediaChunk(com.google.android.exoplayer2.source.chunk.SingleSampleMediaChunk) RangedUri(com.google.android.exoplayer2.source.dash.manifest.RangedUri) DataSpec(com.google.android.exoplayer2.upstream.DataSpec) Representation(com.google.android.exoplayer2.source.dash.manifest.Representation) ContainerMediaChunk(com.google.android.exoplayer2.source.chunk.ContainerMediaChunk)

Example 8 with Segment

use of com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment in project ExoPlayer by google.

the class HlsPlaylistParser method parseMediaPlaylist.

private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String baseUri) throws IOException {
    @HlsMediaPlaylist.PlaylistType int playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_UNKNOWN;
    long startOffsetUs = C.TIME_UNSET;
    int mediaSequence = 0;
    // Default version == 1.
    int version = 1;
    long targetDurationUs = C.TIME_UNSET;
    boolean hasEndTag = false;
    Segment initializationSegment = null;
    List<Segment> segments = new ArrayList<>();
    long segmentDurationUs = 0;
    boolean hasDiscontinuitySequence = false;
    int playlistDiscontinuitySequence = 0;
    int relativeDiscontinuitySequence = 0;
    long playlistStartTimeUs = 0;
    long segmentStartTimeUs = 0;
    long segmentByteRangeOffset = 0;
    long segmentByteRangeLength = C.LENGTH_UNSET;
    int segmentMediaSequence = 0;
    boolean isEncrypted = false;
    String encryptionKeyUri = null;
    String encryptionIV = null;
    String line;
    while (iterator.hasNext()) {
        line = iterator.next();
        if (line.startsWith(TAG_PLAYLIST_TYPE)) {
            String playlistTypeString = parseStringAttr(line, REGEX_PLAYLIST_TYPE);
            if ("VOD".equals(playlistTypeString)) {
                playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_VOD;
            } else if ("EVENT".equals(playlistTypeString)) {
                playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_EVENT;
            } else {
                throw new ParserException("Illegal playlist type: " + playlistTypeString);
            }
        } else if (line.startsWith(TAG_START)) {
            startOffsetUs = (long) (parseDoubleAttr(line, REGEX_TIME_OFFSET) * C.MICROS_PER_SECOND);
        } else if (line.startsWith(TAG_INIT_SEGMENT)) {
            String uri = parseStringAttr(line, REGEX_URI);
            String byteRange = parseOptionalStringAttr(line, REGEX_ATTR_BYTERANGE);
            if (byteRange != null) {
                String[] splitByteRange = byteRange.split("@");
                segmentByteRangeLength = Long.parseLong(splitByteRange[0]);
                if (splitByteRange.length > 1) {
                    segmentByteRangeOffset = Long.parseLong(splitByteRange[1]);
                }
            }
            initializationSegment = new Segment(uri, segmentByteRangeOffset, segmentByteRangeLength);
            segmentByteRangeOffset = 0;
            segmentByteRangeLength = C.LENGTH_UNSET;
        } else if (line.startsWith(TAG_TARGET_DURATION)) {
            targetDurationUs = parseIntAttr(line, REGEX_TARGET_DURATION) * C.MICROS_PER_SECOND;
        } else if (line.startsWith(TAG_MEDIA_SEQUENCE)) {
            mediaSequence = parseIntAttr(line, REGEX_MEDIA_SEQUENCE);
            segmentMediaSequence = mediaSequence;
        } else if (line.startsWith(TAG_VERSION)) {
            version = parseIntAttr(line, REGEX_VERSION);
        } else if (line.startsWith(TAG_MEDIA_DURATION)) {
            segmentDurationUs = (long) (parseDoubleAttr(line, REGEX_MEDIA_DURATION) * C.MICROS_PER_SECOND);
        } else if (line.startsWith(TAG_KEY)) {
            String method = parseStringAttr(line, REGEX_METHOD);
            isEncrypted = METHOD_AES128.equals(method);
            if (isEncrypted) {
                encryptionKeyUri = parseStringAttr(line, REGEX_URI);
                encryptionIV = parseOptionalStringAttr(line, REGEX_IV);
            } else {
                encryptionKeyUri = null;
                encryptionIV = null;
            }
        } else if (line.startsWith(TAG_BYTERANGE)) {
            String byteRange = parseStringAttr(line, REGEX_BYTERANGE);
            String[] splitByteRange = byteRange.split("@");
            segmentByteRangeLength = Long.parseLong(splitByteRange[0]);
            if (splitByteRange.length > 1) {
                segmentByteRangeOffset = Long.parseLong(splitByteRange[1]);
            }
        } else if (line.startsWith(TAG_DISCONTINUITY_SEQUENCE)) {
            hasDiscontinuitySequence = true;
            playlistDiscontinuitySequence = Integer.parseInt(line.substring(line.indexOf(':') + 1));
        } else if (line.equals(TAG_DISCONTINUITY)) {
            relativeDiscontinuitySequence++;
        } else if (line.startsWith(TAG_PROGRAM_DATE_TIME)) {
            if (playlistStartTimeUs == 0) {
                long programDatetimeUs = C.msToUs(Util.parseXsDateTime(line.substring(line.indexOf(':') + 1)));
                playlistStartTimeUs = programDatetimeUs - segmentStartTimeUs;
            }
        } else if (!line.startsWith("#")) {
            String segmentEncryptionIV;
            if (!isEncrypted) {
                segmentEncryptionIV = null;
            } else if (encryptionIV != null) {
                segmentEncryptionIV = encryptionIV;
            } else {
                segmentEncryptionIV = Integer.toHexString(segmentMediaSequence);
            }
            segmentMediaSequence++;
            if (segmentByteRangeLength == C.LENGTH_UNSET) {
                segmentByteRangeOffset = 0;
            }
            segments.add(new Segment(line, segmentDurationUs, relativeDiscontinuitySequence, segmentStartTimeUs, isEncrypted, encryptionKeyUri, segmentEncryptionIV, segmentByteRangeOffset, segmentByteRangeLength));
            segmentStartTimeUs += segmentDurationUs;
            segmentDurationUs = 0;
            if (segmentByteRangeLength != C.LENGTH_UNSET) {
                segmentByteRangeOffset += segmentByteRangeLength;
            }
            segmentByteRangeLength = C.LENGTH_UNSET;
        } else if (line.equals(TAG_ENDLIST)) {
            hasEndTag = true;
        }
    }
    return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, playlistStartTimeUs, hasDiscontinuitySequence, playlistDiscontinuitySequence, mediaSequence, version, targetDurationUs, hasEndTag, playlistStartTimeUs != 0, initializationSegment, segments);
}
Also used : ParserException(com.google.android.exoplayer2.ParserException) ArrayList(java.util.ArrayList) Segment(com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment)

Example 9 with Segment

use of com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment in project ExoPlayer by google.

the class HlsPlaylistTracker method getLoadedPlaylistDiscontinuitySequence.

private int getLoadedPlaylistDiscontinuitySequence(HlsMediaPlaylist oldPlaylist, HlsMediaPlaylist loadedPlaylist) {
    if (loadedPlaylist.hasDiscontinuitySequence) {
        return loadedPlaylist.discontinuitySequence;
    }
    // TODO: Improve cross-playlist discontinuity adjustment.
    int primaryUrlDiscontinuitySequence = primaryUrlSnapshot != null ? primaryUrlSnapshot.discontinuitySequence : 0;
    if (oldPlaylist == null) {
        return primaryUrlDiscontinuitySequence;
    }
    Segment firstOldOverlappingSegment = getFirstOldOverlappingSegment(oldPlaylist, loadedPlaylist);
    if (firstOldOverlappingSegment != null) {
        return oldPlaylist.discontinuitySequence + firstOldOverlappingSegment.relativeDiscontinuitySequence - loadedPlaylist.segments.get(0).relativeDiscontinuitySequence;
    }
    return primaryUrlDiscontinuitySequence;
}
Also used : Segment(com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment)

Aggregations

Segment (com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment)5 DataSpec (com.google.android.exoplayer2.upstream.DataSpec)3 Uri (android.net.Uri)2 ParserException (com.google.android.exoplayer2.ParserException)2 BehindLiveWindowException (com.google.android.exoplayer2.source.BehindLiveWindowException)2 RangedUri (com.google.android.exoplayer2.source.dash.manifest.RangedUri)2 Representation (com.google.android.exoplayer2.source.dash.manifest.Representation)2 DefaultExtractorInput (com.google.android.exoplayer2.extractor.DefaultExtractorInput)1 ExtractorInput (com.google.android.exoplayer2.extractor.ExtractorInput)1 SeekMap (com.google.android.exoplayer2.extractor.SeekMap)1 ContainerMediaChunk (com.google.android.exoplayer2.source.chunk.ContainerMediaChunk)1 SingleSampleMediaChunk (com.google.android.exoplayer2.source.chunk.SingleSampleMediaChunk)1 HlsUrl (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl)1 HlsMediaPlaylist (com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist)1 LongArray (com.google.android.exoplayer2.util.LongArray)1 TimestampAdjuster (com.google.android.exoplayer2.util.TimestampAdjuster)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 ArrayList (java.util.ArrayList)1