Search in sources :

Example 6 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant in project twicalico by moko256.

the class ImagePagerChildFragment method onCreateView.

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    FlingLayout view = (FlingLayout) inflater.inflate(R.layout.fragment_image_pager_child, null);
    view.setDismissListener(() -> {
        getActivity().finish();
        return Unit.INSTANCE;
    });
    view.setPositionChangeListener((Integer top, Integer left, Float dragRangeRate) -> {
        view.setBackgroundColor(Color.argb(Math.round(255 * (1.0F - dragRangeRate)), 0, 0, 0));
        return Unit.INSTANCE;
    });
    MediaEntity mediaEntity;
    if (getArguments() == null)
        return view;
    mediaEntity = (MediaEntity) getArguments().getSerializable(FRAG_MEDIA_ENTITY);
    if (mediaEntity == null)
        return view;
    switch(mediaEntity.getType()) {
        case "video":
            String videoPath = null;
            boolean isHls = false;
            for (MediaEntity.Variant variant : mediaEntity.getVideoVariants()) {
                if (variant.getContentType().equals("application/x-mpegURL")) {
                    videoPath = variant.getUrl();
                    isHls = true;
                }
            }
            if (videoPath == null) {
                videoPath = mediaEntity.getVideoVariants()[0].getUrl();
            }
            videoPlayView = view.findViewById(R.id.fragment_image_pager_video);
            videoPlayView.setVisibility(View.VISIBLE);
            videoPlayView.setControllerVisibilityListener(visibility -> {
                if (visibility != View.VISIBLE) {
                    hideSystemUI();
                } else {
                    showSystemUI();
                }
            });
            getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                    ((AppCompatActivity) getActivity()).getSupportActionBar().show();
                    videoPlayView.showController();
                }
            });
            player = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(getContext()), new DefaultTrackSelector(new AdaptiveTrackSelection.Factory(new DefaultBandwidthMeter(), Integer.MAX_VALUE, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION)), new DefaultLoadControl());
            if (savedInstanceState != null) {
                player.seekTo(savedInstanceState.getLong("video_time", 0));
            }
            videoPlayView.setPlayer(player);
            player.prepare((isHls) ? new HlsMediaSource.Factory(new OkHttpDataSourceFactory(GlobalApplication.getOkHttpClient(), getResources().getText(R.string.app_name).toString(), null)).createMediaSource(Uri.parse(videoPath)) : new ExtractorMediaSource.Factory(new OkHttpDataSourceFactory(GlobalApplication.getOkHttpClient(), getResources().getText(R.string.app_name).toString(), null)).createMediaSource(Uri.parse(mediaEntity.getVideoVariants()[0].getUrl()), new Handler(), null));
            break;
        case "animated_gif":
            videoPlayView = view.findViewById(R.id.fragment_image_pager_video);
            videoPlayView.setVisibility(View.VISIBLE);
            videoPlayView.setControllerVisibilityListener(visibility -> {
                if (visibility != View.VISIBLE) {
                    hideSystemUI();
                } else {
                    showSystemUI();
                }
            });
            getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                    ((AppCompatActivity) getActivity()).getSupportActionBar().show();
                    videoPlayView.showController();
                }
            });
            player = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(getContext()), new DefaultTrackSelector(new AdaptiveTrackSelection.Factory(new DefaultBandwidthMeter(), Integer.MAX_VALUE, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION)), new DefaultLoadControl());
            if (savedInstanceState != null) {
                player.seekTo(savedInstanceState.getLong("video_time", 0));
            }
            videoPlayView.setPlayer(player);
            player.prepare(new LoopingMediaSource(new ExtractorMediaSource.Factory(new OkHttpDataSourceFactory(GlobalApplication.getOkHttpClient(), getResources().getText(R.string.app_name).toString(), null)).createMediaSource(Uri.parse(mediaEntity.getVideoVariants()[0].getUrl()), new Handler(), null)));
            player.setPlayWhenReady(true);
            break;
        case "photo":
        default:
            getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                    ((AppCompatActivity) getActivity()).getSupportActionBar().show();
                }
            });
            imageView = view.findViewById(R.id.fragment_image_pager_image);
            imageView.setVisibility(View.VISIBLE);
            imageView.setOnClickListener(v -> {
                if ((getActivity().getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                    hideSystemUI();
                } else {
                    showSystemUI();
                }
            });
            imageView.setOnScaleChangeListener((float scaleFactor, float focusX, float focusY) -> view.setDragEnabled(scaleFactor <= 1F));
            GlideApp.with(this).load(TwitterStringUtils.convertLargeImageUrl(mediaEntity.getMediaURLHttps())).fitCenter().thumbnail(GlideApp.with(this).load(TwitterStringUtils.convertSmallImageUrl(mediaEntity.getMediaURLHttps())).fitCenter()).into(imageView);
            break;
    }
    return view;
}
Also used : DefaultBandwidthMeter(com.google.android.exoplayer2.upstream.DefaultBandwidthMeter) Handler(android.os.Handler) ExtractorMediaSource(com.google.android.exoplayer2.source.ExtractorMediaSource) DefaultLoadControl(com.google.android.exoplayer2.DefaultLoadControl) HlsMediaSource(com.google.android.exoplayer2.source.hls.HlsMediaSource) AdaptiveTrackSelection(com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection) DefaultTrackSelector(com.google.android.exoplayer2.trackselection.DefaultTrackSelector) OkHttpDataSourceFactory(com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory) DefaultRenderersFactory(com.google.android.exoplayer2.DefaultRenderersFactory) MediaEntity(twitter4j.MediaEntity) FlingLayout(com.github.chuross.flinglayout.FlingLayout) LoopingMediaSource(com.google.android.exoplayer2.source.LoopingMediaSource) Nullable(android.support.annotation.Nullable)

Example 7 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant in project ExoPlayer by google.

the class HlsSampleStreamWrapper method deriveFormat.

/**
 * Derives a track sample format from the corresponding format in the multivariant playlist, and a
 * sample format that may have been obtained from a chunk belonging to a different track in the
 * same track group.
 *
 * <p>Note: Since the sample format may have been obtained from a chunk belonging to a different
 * track, it should not be used as a source for data that may vary between tracks.
 *
 * @param playlistFormat The format information obtained from the multivariant playlist.
 * @param sampleFormat The format information obtained from samples within a chunk. The chunk may
 *     belong to a different track in the same track group.
 * @param propagateBitrates Whether the bitrates from the playlist format should be included in
 *     the derived format.
 * @return The derived track format.
 */
private static Format deriveFormat(@Nullable Format playlistFormat, Format sampleFormat, boolean propagateBitrates) {
    if (playlistFormat == null) {
        return sampleFormat;
    }
    int sampleTrackType = MimeTypes.getTrackType(sampleFormat.sampleMimeType);
    @Nullable String sampleMimeType;
    @Nullable String codecs;
    if (Util.getCodecCountOfType(playlistFormat.codecs, sampleTrackType) == 1) {
        // We can unequivocally map this track to a playlist variant because only one codec string
        // matches this track's type.
        codecs = Util.getCodecsOfType(playlistFormat.codecs, sampleTrackType);
        sampleMimeType = MimeTypes.getMediaMimeType(codecs);
    } else {
        // The variant assigns more than one codec string to this track. We choose whichever codec
        // string matches the sample mime type. This can happen when different languages are encoded
        // using different codecs.
        codecs = MimeTypes.getCodecsCorrespondingToMimeType(playlistFormat.codecs, sampleFormat.sampleMimeType);
        sampleMimeType = sampleFormat.sampleMimeType;
    }
    Format.Builder formatBuilder = sampleFormat.buildUpon().setId(playlistFormat.id).setLabel(playlistFormat.label).setLanguage(playlistFormat.language).setSelectionFlags(playlistFormat.selectionFlags).setRoleFlags(playlistFormat.roleFlags).setAverageBitrate(propagateBitrates ? playlistFormat.averageBitrate : Format.NO_VALUE).setPeakBitrate(propagateBitrates ? playlistFormat.peakBitrate : Format.NO_VALUE).setCodecs(codecs);
    if (sampleTrackType == C.TRACK_TYPE_VIDEO) {
        formatBuilder.setWidth(playlistFormat.width).setHeight(playlistFormat.height).setFrameRate(playlistFormat.frameRate);
    }
    if (sampleMimeType != null) {
        formatBuilder.setSampleMimeType(sampleMimeType);
    }
    if (playlistFormat.channelCount != Format.NO_VALUE && sampleTrackType == C.TRACK_TYPE_AUDIO) {
        formatBuilder.setChannelCount(playlistFormat.channelCount);
    }
    if (playlistFormat.metadata != null) {
        Metadata metadata = playlistFormat.metadata;
        if (sampleFormat.metadata != null) {
            metadata = sampleFormat.metadata.copyWithAppendedEntriesFrom(metadata);
        }
        formatBuilder.setMetadata(metadata);
    }
    return formatBuilder.build();
}
Also used : Format(com.google.android.exoplayer2.Format) Metadata(com.google.android.exoplayer2.metadata.Metadata) Nullable(androidx.annotation.Nullable)

Example 8 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant in project ExoPlayer by google.

the class HlsSampleStreamWrapper method buildTracksFromSampleStreams.

/**
 * 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 multivariant 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 multivariant 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>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>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.
 * </ul>
 */
@EnsuresNonNull({ "trackGroups", "optionalTrackGroups", "trackGroupToSampleQueueIndex" })
private void buildTracksFromSampleStreams() {
    // Iterate through the extractor tracks to discover the "primary" track type, and the index
    // of the single track of this type.
    int primaryExtractorTrackType = C.TRACK_TYPE_NONE;
    int primaryExtractorTrackIndex = C.INDEX_UNSET;
    int extractorTrackCount = sampleQueues.length;
    for (int i = 0; i < extractorTrackCount; i++) {
        @Nullable String sampleMimeType = Assertions.checkStateNotNull(sampleQueues[i].getUpstreamFormat()).sampleMimeType;
        int trackType;
        if (MimeTypes.isVideo(sampleMimeType)) {
            trackType = C.TRACK_TYPE_VIDEO;
        } else if (MimeTypes.isAudio(sampleMimeType)) {
            trackType = C.TRACK_TYPE_AUDIO;
        } else if (MimeTypes.isText(sampleMimeType)) {
            trackType = C.TRACK_TYPE_TEXT;
        } else {
            trackType = C.TRACK_TYPE_NONE;
        }
        if (getTrackTypeScore(trackType) > getTrackTypeScore(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;
    trackGroupToSampleQueueIndex = new int[extractorTrackCount];
    for (int i = 0; i < extractorTrackCount; i++) {
        trackGroupToSampleQueueIndex[i] = i;
    }
    // Construct the set of exposed track groups.
    TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
    for (int i = 0; i < extractorTrackCount; i++) {
        Format sampleFormat = Assertions.checkStateNotNull(sampleQueues[i].getUpstreamFormat());
        if (i == primaryExtractorTrackIndex) {
            Format[] formats = new Format[chunkSourceTrackCount];
            for (int j = 0; j < chunkSourceTrackCount; j++) {
                Format playlistFormat = chunkSourceTrackGroup.getFormat(j);
                if (primaryExtractorTrackType == C.TRACK_TYPE_AUDIO && muxedAudioFormat != null) {
                    playlistFormat = playlistFormat.withManifestFormatInfo(muxedAudioFormat);
                }
                // If there's only a single variant (chunkSourceTrackCount == 1) then we can safely
                // retain all fields from sampleFormat. Else we need to use deriveFormat to retain only
                // the fields that will be the same for all variants.
                formats[j] = chunkSourceTrackCount == 1 ? sampleFormat.withManifestFormatInfo(playlistFormat) : deriveFormat(playlistFormat, sampleFormat, /* propagateBitrates= */
                true);
            }
            trackGroups[i] = new TrackGroup(uid, formats);
            primaryTrackGroupIndex = i;
        } else {
            @Nullable Format playlistFormat = primaryExtractorTrackType == C.TRACK_TYPE_VIDEO && MimeTypes.isAudio(sampleFormat.sampleMimeType) ? muxedAudioFormat : null;
            String muxedTrackGroupId = uid + ":muxed:" + (i < primaryExtractorTrackIndex ? i : i - 1);
            trackGroups[i] = new TrackGroup(muxedTrackGroupId, deriveFormat(playlistFormat, sampleFormat, /* propagateBitrates= */
            false));
        }
    }
    this.trackGroups = createTrackGroupArrayWithDrmInfo(trackGroups);
    Assertions.checkState(optionalTrackGroups == null);
    optionalTrackGroups = Collections.emptySet();
}
Also used : Format(com.google.android.exoplayer2.Format) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) Nullable(androidx.annotation.Nullable) EnsuresNonNull(org.checkerframework.checker.nullness.qual.EnsuresNonNull)

Example 9 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant in project ExoPlayer by google.

the class HlsMediaPeriod method buildAndPrepareMainSampleStreamWrapper.

/**
 * This method creates and starts preparation of the main {@link HlsSampleStreamWrapper}.
 *
 * <p>The main sample stream wrapper is the first element of {@link #sampleStreamWrappers}. It
 * provides {@link SampleStream}s for the variant urls in the multivariant playlist. It may be
 * adaptive and may contain multiple muxed tracks.
 *
 * <p>If chunkless preparation is allowed, the media period will try preparation without segment
 * downloads. This is only possible if variants contain the CODECS attribute. If not, traditional
 * preparation with segment downloads will take place. The following points apply to chunkless
 * preparation:
 *
 * <ul>
 *   <li>A muxed audio track will be exposed if the codecs list contain an audio entry and the
 *       multivariant playlist either contains an EXT-X-MEDIA tag without the URI attribute or
 *       does not contain any EXT-X-MEDIA tag.
 *   <li>Closed captions will only be exposed if they are declared by the multivariant playlist.
 *   <li>An ID3 track is exposed preemptively, in case the segments contain an ID3 track.
 * </ul>
 *
 * @param multivariantPlaylist The HLS multivariant playlist.
 * @param positionUs If preparation requires any chunk downloads, the position in microseconds at
 *     which downloading should start. Ignored otherwise.
 * @param sampleStreamWrappers List to which the built main sample stream wrapper should be added.
 * @param manifestUrlIndicesPerWrapper List to which the selected variant indices should be added.
 * @param overridingDrmInitData Overriding {@link DrmInitData}, keyed by protection scheme type
 *     (i.e. {@link DrmInitData#schemeType}).
 */
private void buildAndPrepareMainSampleStreamWrapper(HlsMultivariantPlaylist multivariantPlaylist, long positionUs, List<HlsSampleStreamWrapper> sampleStreamWrappers, List<int[]> manifestUrlIndicesPerWrapper, Map<String, DrmInitData> overridingDrmInitData) {
    int[] variantTypes = new int[multivariantPlaylist.variants.size()];
    int videoVariantCount = 0;
    int audioVariantCount = 0;
    for (int i = 0; i < multivariantPlaylist.variants.size(); i++) {
        Variant variant = multivariantPlaylist.variants.get(i);
        Format format = variant.format;
        if (format.height > 0 || Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_VIDEO) != null) {
            variantTypes[i] = C.TRACK_TYPE_VIDEO;
            videoVariantCount++;
        } else if (Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_AUDIO) != null) {
            variantTypes[i] = C.TRACK_TYPE_AUDIO;
            audioVariantCount++;
        } else {
            variantTypes[i] = C.TRACK_TYPE_UNKNOWN;
        }
    }
    boolean useVideoVariantsOnly = false;
    boolean useNonAudioVariantsOnly = false;
    int selectedVariantsCount = variantTypes.length;
    if (videoVariantCount > 0) {
        // We've identified some variants as definitely containing video. Assume variants within the
        // multivariant playlist are marked consistently, and hence that we have the full set. Filter
        // out any other variants, which are likely to be audio only.
        useVideoVariantsOnly = true;
        selectedVariantsCount = videoVariantCount;
    } else if (audioVariantCount < variantTypes.length) {
        // We've identified some variants, but not all, as being audio only. Filter them out to leave
        // the remaining variants, which are likely to contain video.
        useNonAudioVariantsOnly = true;
        selectedVariantsCount = variantTypes.length - audioVariantCount;
    }
    Uri[] selectedPlaylistUrls = new Uri[selectedVariantsCount];
    Format[] selectedPlaylistFormats = new Format[selectedVariantsCount];
    int[] selectedVariantIndices = new int[selectedVariantsCount];
    int outIndex = 0;
    for (int i = 0; i < multivariantPlaylist.variants.size(); i++) {
        if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO) && (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) {
            Variant variant = multivariantPlaylist.variants.get(i);
            selectedPlaylistUrls[outIndex] = variant.url;
            selectedPlaylistFormats[outIndex] = variant.format;
            selectedVariantIndices[outIndex++] = i;
        }
    }
    String codecs = selectedPlaylistFormats[0].codecs;
    int numberOfVideoCodecs = Util.getCodecCountOfType(codecs, C.TRACK_TYPE_VIDEO);
    int numberOfAudioCodecs = Util.getCodecCountOfType(codecs, C.TRACK_TYPE_AUDIO);
    boolean codecsStringAllowsChunklessPreparation = numberOfAudioCodecs <= 1 && numberOfVideoCodecs <= 1 && numberOfAudioCodecs + numberOfVideoCodecs > 0;
    @C.TrackType int trackType = !useVideoVariantsOnly && numberOfAudioCodecs > 0 ? C.TRACK_TYPE_AUDIO : C.TRACK_TYPE_DEFAULT;
    String sampleStreamWrapperUid = "main";
    HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(sampleStreamWrapperUid, trackType, selectedPlaylistUrls, selectedPlaylistFormats, multivariantPlaylist.muxedAudioFormat, multivariantPlaylist.muxedCaptionFormats, overridingDrmInitData, positionUs);
    sampleStreamWrappers.add(sampleStreamWrapper);
    manifestUrlIndicesPerWrapper.add(selectedVariantIndices);
    if (allowChunklessPreparation && codecsStringAllowsChunklessPreparation) {
        List<TrackGroup> muxedTrackGroups = new ArrayList<>();
        if (numberOfVideoCodecs > 0) {
            Format[] videoFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < videoFormats.length; i++) {
                videoFormats[i] = deriveVideoFormat(selectedPlaylistFormats[i]);
            }
            muxedTrackGroups.add(new TrackGroup(sampleStreamWrapperUid, videoFormats));
            if (numberOfAudioCodecs > 0 && (multivariantPlaylist.muxedAudioFormat != null || multivariantPlaylist.audios.isEmpty())) {
                muxedTrackGroups.add(new TrackGroup(/* id= */
                sampleStreamWrapperUid + ":audio", deriveAudioFormat(selectedPlaylistFormats[0], multivariantPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                false)));
            }
            List<Format> ccFormats = multivariantPlaylist.muxedCaptionFormats;
            if (ccFormats != null) {
                for (int i = 0; i < ccFormats.size(); i++) {
                    String ccId = sampleStreamWrapperUid + ":cc:" + i;
                    muxedTrackGroups.add(new TrackGroup(ccId, ccFormats.get(i)));
                }
            }
        } else /* numberOfAudioCodecs > 0 */
        {
            // Variants only contain audio.
            Format[] audioFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < audioFormats.length; i++) {
                audioFormats[i] = deriveAudioFormat(/* variantFormat= */
                selectedPlaylistFormats[i], multivariantPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                true);
            }
            muxedTrackGroups.add(new TrackGroup(sampleStreamWrapperUid, audioFormats));
        }
        TrackGroup id3TrackGroup = new TrackGroup(/* id= */
        sampleStreamWrapperUid + ":id3", new Format.Builder().setId("ID3").setSampleMimeType(MimeTypes.APPLICATION_ID3).build());
        muxedTrackGroups.add(id3TrackGroup);
        sampleStreamWrapper.prepareWithMultivariantPlaylistInfo(muxedTrackGroups.toArray(new TrackGroup[0]), /* primaryTrackGroupIndex= */
        0, /* optionalTrackGroupsIndices...= */
        muxedTrackGroups.indexOf(id3TrackGroup));
    }
}
Also used : ArrayList(java.util.ArrayList) Uri(android.net.Uri) Variant(com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant) Format(com.google.android.exoplayer2.Format) TrackGroup(com.google.android.exoplayer2.source.TrackGroup)

Example 10 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant in project ExoPlayer by google.

the class HlsMediaPeriod method selectTracks.

@Override
public long selectTracks(@NullableType ExoTrackSelection[] selections, boolean[] mayRetainStreamFlags, @NullableType SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
    // Map each selection and stream onto a child period index.
    int[] streamChildIndices = new int[selections.length];
    int[] selectionChildIndices = new int[selections.length];
    for (int i = 0; i < selections.length; i++) {
        streamChildIndices[i] = streams[i] == null ? C.INDEX_UNSET : streamWrapperIndices.get(streams[i]);
        selectionChildIndices[i] = C.INDEX_UNSET;
        if (selections[i] != null) {
            TrackGroup trackGroup = selections[i].getTrackGroup();
            for (int j = 0; j < sampleStreamWrappers.length; j++) {
                if (sampleStreamWrappers[j].getTrackGroups().indexOf(trackGroup) != C.INDEX_UNSET) {
                    selectionChildIndices[i] = j;
                    break;
                }
            }
        }
    }
    boolean forceReset = false;
    streamWrapperIndices.clear();
    // Select tracks for each child, copying the resulting streams back into a new streams array.
    SampleStream[] newStreams = new SampleStream[selections.length];
    @NullableType SampleStream[] childStreams = new SampleStream[selections.length];
    @NullableType ExoTrackSelection[] childSelections = new ExoTrackSelection[selections.length];
    int newEnabledSampleStreamWrapperCount = 0;
    HlsSampleStreamWrapper[] newEnabledSampleStreamWrappers = new HlsSampleStreamWrapper[sampleStreamWrappers.length];
    for (int i = 0; i < sampleStreamWrappers.length; i++) {
        for (int j = 0; j < selections.length; j++) {
            childStreams[j] = streamChildIndices[j] == i ? streams[j] : null;
            childSelections[j] = selectionChildIndices[j] == i ? selections[j] : null;
        }
        HlsSampleStreamWrapper sampleStreamWrapper = sampleStreamWrappers[i];
        boolean wasReset = sampleStreamWrapper.selectTracks(childSelections, mayRetainStreamFlags, childStreams, streamResetFlags, positionUs, forceReset);
        boolean wrapperEnabled = false;
        for (int j = 0; j < selections.length; j++) {
            SampleStream childStream = childStreams[j];
            if (selectionChildIndices[j] == i) {
                // Assert that the child provided a stream for the selection.
                Assertions.checkNotNull(childStream);
                newStreams[j] = childStream;
                wrapperEnabled = true;
                streamWrapperIndices.put(childStream, i);
            } else if (streamChildIndices[j] == i) {
                // Assert that the child cleared any previous stream.
                Assertions.checkState(childStream == null);
            }
        }
        if (wrapperEnabled) {
            newEnabledSampleStreamWrappers[newEnabledSampleStreamWrapperCount] = sampleStreamWrapper;
            if (newEnabledSampleStreamWrapperCount++ == 0) {
                // The first enabled wrapper is always allowed to initialize timestamp adjusters. Note
                // that the first wrapper will correspond to a variant, or else an audio rendition, or
                // else a text rendition, in that order.
                sampleStreamWrapper.setIsTimestampMaster(true);
                if (wasReset || enabledSampleStreamWrappers.length == 0 || sampleStreamWrapper != enabledSampleStreamWrappers[0]) {
                    // The wrapper responsible for initializing the timestamp adjusters was reset or
                    // changed. We need to reset the timestamp adjuster provider and all other wrappers.
                    timestampAdjusterProvider.reset();
                    forceReset = true;
                }
            } else {
                // Additional wrappers are also allowed to initialize timestamp adjusters if they contain
                // audio or video, since they are expected to contain dense samples. Text wrappers are not
                // permitted except in the case above in which no variant or audio rendition wrappers are
                // enabled.
                sampleStreamWrapper.setIsTimestampMaster(i < audioVideoSampleStreamWrapperCount);
            }
        }
    }
    // Copy the new streams back into the streams array.
    System.arraycopy(newStreams, 0, streams, 0, newStreams.length);
    // Update the local state.
    enabledSampleStreamWrappers = Util.nullSafeArrayCopy(newEnabledSampleStreamWrappers, newEnabledSampleStreamWrapperCount);
    compositeSequenceableLoader = compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(enabledSampleStreamWrappers);
    return positionUs;
}
Also used : ExoTrackSelection(com.google.android.exoplayer2.trackselection.ExoTrackSelection) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) NullableType(org.checkerframework.checker.nullness.compatqual.NullableType) SampleStream(com.google.android.exoplayer2.source.SampleStream)

Aggregations

Format (com.google.android.exoplayer2.Format)7 TrackGroup (com.google.android.exoplayer2.source.TrackGroup)5 Variant (com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant)5 ArrayList (java.util.ArrayList)4 Uri (android.net.Uri)3 Nullable (androidx.annotation.Nullable)3 Test (org.junit.Test)3 Metadata (com.google.android.exoplayer2.metadata.Metadata)2 TrackGroupArray (com.google.android.exoplayer2.source.TrackGroupArray)2 HlsUrl (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl)2 ExoTrackSelection (com.google.android.exoplayer2.trackselection.ExoTrackSelection)2 Handler (android.os.Handler)1 Nullable (android.support.annotation.Nullable)1 FlingLayout (com.github.chuross.flinglayout.FlingLayout)1 DefaultLoadControl (com.google.android.exoplayer2.DefaultLoadControl)1 DefaultRenderersFactory (com.google.android.exoplayer2.DefaultRenderersFactory)1 DrmInitData (com.google.android.exoplayer2.drm.DrmInitData)1 SchemeData (com.google.android.exoplayer2.drm.DrmInitData.SchemeData)1 OkHttpDataSourceFactory (com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory)1 StreamKey (com.google.android.exoplayer2.offline.StreamKey)1