Search in sources :

Example 6 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project Telegram-FOSS by Telegram-FOSS-Team.

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 master 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
 *       master 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 master playlist.
 *   <li>An ID3 track is exposed preemptively, in case the segments contain an ID3 track.
 * </ul>
 *
 * @param masterPlaylist The HLS master 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(HlsMasterPlaylist masterPlaylist, long positionUs, List<HlsSampleStreamWrapper> sampleStreamWrappers, List<int[]> manifestUrlIndicesPerWrapper, Map<String, DrmInitData> overridingDrmInitData) {
    int[] variantTypes = new int[masterPlaylist.variants.size()];
    int videoVariantCount = 0;
    int audioVariantCount = 0;
    for (int i = 0; i < masterPlaylist.variants.size(); i++) {
        Variant variant = masterPlaylist.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
        // master 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 < masterPlaylist.variants.size(); i++) {
        if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO) && (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) {
            Variant variant = masterPlaylist.variants.get(i);
            selectedPlaylistUrls[outIndex] = variant.url;
            selectedPlaylistFormats[outIndex] = variant.format;
            selectedVariantIndices[outIndex++] = i;
        }
    }
    String codecs = selectedPlaylistFormats[0].codecs;
    HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, selectedPlaylistUrls, selectedPlaylistFormats, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormats, overridingDrmInitData, positionUs);
    sampleStreamWrappers.add(sampleStreamWrapper);
    manifestUrlIndicesPerWrapper.add(selectedVariantIndices);
    if (allowChunklessPreparation && codecs != null) {
        boolean variantsContainVideoCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_VIDEO) != null;
        boolean variantsContainAudioCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_AUDIO) != null;
        List<TrackGroup> muxedTrackGroups = new ArrayList<>();
        if (variantsContainVideoCodecs) {
            Format[] videoFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < videoFormats.length; i++) {
                videoFormats[i] = deriveVideoFormat(selectedPlaylistFormats[i]);
            }
            muxedTrackGroups.add(new TrackGroup(videoFormats));
            if (variantsContainAudioCodecs && (masterPlaylist.muxedAudioFormat != null || masterPlaylist.audios.isEmpty())) {
                muxedTrackGroups.add(new TrackGroup(deriveAudioFormat(selectedPlaylistFormats[0], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                false)));
            }
            List<Format> ccFormats = masterPlaylist.muxedCaptionFormats;
            if (ccFormats != null) {
                for (int i = 0; i < ccFormats.size(); i++) {
                    muxedTrackGroups.add(new TrackGroup(ccFormats.get(i)));
                }
            }
        } else if (variantsContainAudioCodecs) {
            // Variants only contain audio.
            Format[] audioFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < audioFormats.length; i++) {
                audioFormats[i] = deriveAudioFormat(/* variantFormat= */
                selectedPlaylistFormats[i], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                true);
            }
            muxedTrackGroups.add(new TrackGroup(audioFormats));
        } else {
            // Variants contain codecs but no video or audio entries could be identified.
            throw new IllegalArgumentException("Unexpected codecs attribute: " + codecs);
        }
        TrackGroup id3TrackGroup = new TrackGroup(Format.createSampleFormat(/* id= */
        "ID3", MimeTypes.APPLICATION_ID3, /* codecs= */
        null, /* bitrate= */
        Format.NO_VALUE, /* drmInitData= */
        null));
        muxedTrackGroups.add(id3TrackGroup);
        sampleStreamWrapper.prepareWithMasterPlaylistInfo(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.HlsMasterPlaylist.Variant) Format(com.google.android.exoplayer2.Format) TrackGroup(com.google.android.exoplayer2.source.TrackGroup)

Example 7 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project hms-wiseplay-demo-exoplayer by HMS-Core.

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 master 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
 *       master 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 master playlist.
 *   <li>An ID3 track is exposed preemptively, in case the segments contain an ID3 track.
 * </ul>
 *
 * @param masterPlaylist The HLS master 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(HlsMasterPlaylist masterPlaylist, long positionUs, List<HlsSampleStreamWrapper> sampleStreamWrappers, List<int[]> manifestUrlIndicesPerWrapper, Map<String, DrmInitData> overridingDrmInitData) {
    int[] variantTypes = new int[masterPlaylist.variants.size()];
    int videoVariantCount = 0;
    int audioVariantCount = 0;
    for (int i = 0; i < masterPlaylist.variants.size(); i++) {
        Variant variant = masterPlaylist.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
        // master 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 < masterPlaylist.variants.size(); i++) {
        if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO) && (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) {
            Variant variant = masterPlaylist.variants.get(i);
            selectedPlaylistUrls[outIndex] = variant.url;
            selectedPlaylistFormats[outIndex] = variant.format;
            selectedVariantIndices[outIndex++] = i;
        }
    }
    String codecs = selectedPlaylistFormats[0].codecs;
    HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, selectedPlaylistUrls, selectedPlaylistFormats, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormats, overridingDrmInitData, positionUs);
    sampleStreamWrappers.add(sampleStreamWrapper);
    manifestUrlIndicesPerWrapper.add(selectedVariantIndices);
    if (allowChunklessPreparation && codecs != null) {
        boolean variantsContainVideoCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_VIDEO) != null;
        boolean variantsContainAudioCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_AUDIO) != null;
        List<TrackGroup> muxedTrackGroups = new ArrayList<>();
        if (variantsContainVideoCodecs) {
            Format[] videoFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < videoFormats.length; i++) {
                videoFormats[i] = deriveVideoFormat(selectedPlaylistFormats[i]);
            }
            muxedTrackGroups.add(new TrackGroup(videoFormats));
            if (variantsContainAudioCodecs && (masterPlaylist.muxedAudioFormat != null || masterPlaylist.audios.isEmpty())) {
                muxedTrackGroups.add(new TrackGroup(deriveAudioFormat(selectedPlaylistFormats[0], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                false)));
            }
            List<Format> ccFormats = masterPlaylist.muxedCaptionFormats;
            if (ccFormats != null) {
                for (int i = 0; i < ccFormats.size(); i++) {
                    muxedTrackGroups.add(new TrackGroup(ccFormats.get(i)));
                }
            }
        } else if (variantsContainAudioCodecs) {
            // Variants only contain audio.
            Format[] audioFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < audioFormats.length; i++) {
                audioFormats[i] = deriveAudioFormat(/* variantFormat= */
                selectedPlaylistFormats[i], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                true);
            }
            muxedTrackGroups.add(new TrackGroup(audioFormats));
        } else {
            // Variants contain codecs but no video or audio entries could be identified.
            throw new IllegalArgumentException("Unexpected codecs attribute: " + codecs);
        }
        TrackGroup id3TrackGroup = new TrackGroup(Format.createSampleFormat(/* id= */
        "ID3", MimeTypes.APPLICATION_ID3, /* codecs= */
        null, /* bitrate= */
        Format.NO_VALUE, /* drmInitData= */
        null));
        muxedTrackGroups.add(id3TrackGroup);
        sampleStreamWrapper.prepareWithMasterPlaylistInfo(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.HlsMasterPlaylist.Variant) Format(com.google.android.exoplayer2.Format) TrackGroup(com.google.android.exoplayer2.source.TrackGroup)

Example 8 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project hms-wiseplay-demo-exoplayer by HMS-Core.

the class HlsMasterPlaylistParserTest method parseMasterPlaylist_withTtmlSubtitle.

@Test
public void parseMasterPlaylist_withTtmlSubtitle() throws IOException {
    HlsMasterPlaylist playlistWithTtmlSubtitle = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_TTML_SUBTITLE);
    HlsMasterPlaylist.Variant variant = playlistWithTtmlSubtitle.variants.get(0);
    Format firstTextFormat = playlistWithTtmlSubtitle.subtitles.get(0).format;
    assertThat(firstTextFormat.id).isEqualTo("sub1:English");
    assertThat(firstTextFormat.containerMimeType).isEqualTo(MimeTypes.APPLICATION_M3U8);
    assertThat(firstTextFormat.sampleMimeType).isEqualTo(MimeTypes.APPLICATION_TTML);
    assertThat(variant.format.codecs).isEqualTo("stpp.ttml.im1t,mp4a.40.2,avc1.66.30");
}
Also used : Format(com.google.android.exoplayer2.Format) Test(org.junit.Test)

Example 9 with Variant

use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project hms-wiseplay-demo-exoplayer by HMS-Core.

the class HlsPlaylistParser method parseMasterPlaylist.

private static HlsMasterPlaylist parseMasterPlaylist(LineIterator iterator, String baseUri) throws IOException {
    HashMap<Uri, ArrayList<VariantInfo>> urlToVariantInfos = new HashMap<>();
    HashMap<String, String> variableDefinitions = new HashMap<>();
    ArrayList<Variant> variants = new ArrayList<>();
    ArrayList<Rendition> videos = new ArrayList<>();
    ArrayList<Rendition> audios = new ArrayList<>();
    ArrayList<Rendition> subtitles = new ArrayList<>();
    ArrayList<Rendition> closedCaptions = new ArrayList<>();
    ArrayList<String> mediaTags = new ArrayList<>();
    ArrayList<DrmInitData> sessionKeyDrmInitData = new ArrayList<>();
    ArrayList<String> tags = new ArrayList<>();
    Format muxedAudioFormat = null;
    List<Format> muxedCaptionFormats = null;
    boolean noClosedCaptions = false;
    boolean hasIndependentSegmentsTag = false;
    String line;
    while (iterator.hasNext()) {
        line = iterator.next();
        if (line.startsWith(TAG_PREFIX)) {
            // We expose all tags through the playlist.
            tags.add(line);
        }
        if (line.startsWith(TAG_DEFINE)) {
            variableDefinitions.put(/* key= */
            parseStringAttr(line, REGEX_NAME, variableDefinitions), /* value= */
            parseStringAttr(line, REGEX_VALUE, variableDefinitions));
        } else if (line.equals(TAG_INDEPENDENT_SEGMENTS)) {
            hasIndependentSegmentsTag = true;
        } else if (line.startsWith(TAG_MEDIA)) {
            // Media tags are parsed at the end to include codec information from #EXT-X-STREAM-INF
            // tags.
            mediaTags.add(line);
        } else if (line.startsWith(TAG_SESSION_KEY)) {
            String keyFormat = parseOptionalStringAttr(line, REGEX_KEYFORMAT, KEYFORMAT_IDENTITY, variableDefinitions);
            SchemeData schemeData = parseDrmSchemeData(line, keyFormat, variableDefinitions);
            if (schemeData != null) {
                String method = parseStringAttr(line, REGEX_METHOD, variableDefinitions);
                String scheme = parseEncryptionScheme(method);
                sessionKeyDrmInitData.add(new DrmInitData(scheme, schemeData));
            }
        } else if (line.startsWith(TAG_STREAM_INF)) {
            noClosedCaptions |= line.contains(ATTR_CLOSED_CAPTIONS_NONE);
            int bitrate = parseIntAttr(line, REGEX_BANDWIDTH);
            // TODO: Plumb this into Format.
            int averageBitrate = parseOptionalIntAttr(line, REGEX_AVERAGE_BANDWIDTH, -1);
            String codecs = parseOptionalStringAttr(line, REGEX_CODECS, variableDefinitions);
            String resolutionString = parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions);
            int width;
            int height;
            if (resolutionString != null) {
                String[] widthAndHeight = resolutionString.split("x");
                width = Integer.parseInt(widthAndHeight[0]);
                height = Integer.parseInt(widthAndHeight[1]);
                if (width <= 0 || height <= 0) {
                    // Resolution string is invalid.
                    width = Format.NO_VALUE;
                    height = Format.NO_VALUE;
                }
            } else {
                width = Format.NO_VALUE;
                height = Format.NO_VALUE;
            }
            float frameRate = Format.NO_VALUE;
            String frameRateString = parseOptionalStringAttr(line, REGEX_FRAME_RATE, variableDefinitions);
            if (frameRateString != null) {
                frameRate = Float.parseFloat(frameRateString);
            }
            String videoGroupId = parseOptionalStringAttr(line, REGEX_VIDEO, variableDefinitions);
            String audioGroupId = parseOptionalStringAttr(line, REGEX_AUDIO, variableDefinitions);
            String subtitlesGroupId = parseOptionalStringAttr(line, REGEX_SUBTITLES, variableDefinitions);
            String closedCaptionsGroupId = parseOptionalStringAttr(line, REGEX_CLOSED_CAPTIONS, variableDefinitions);
            if (!iterator.hasNext()) {
                throw new ParserException("#EXT-X-STREAM-INF tag must be followed by another line");
            }
            line = replaceVariableReferences(iterator.next(), // #EXT-X-STREAM-INF's URI.
            variableDefinitions);
            Uri uri = UriUtil.resolveToUri(baseUri, line);
            Format format = Format.createVideoContainerFormat(/* id= */
            Integer.toString(variants.size()), /* label= */
            null, /* containerMimeType= */
            MimeTypes.APPLICATION_M3U8, /* sampleMimeType= */
            null, codecs, /* metadata= */
            null, bitrate, width, height, frameRate, /* initializationData= */
            null, /* selectionFlags= */
            0, /* roleFlags= */
            0);
            Variant variant = new Variant(uri, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId);
            variants.add(variant);
            ArrayList<VariantInfo> variantInfosForUrl = urlToVariantInfos.get(uri);
            if (variantInfosForUrl == null) {
                variantInfosForUrl = new ArrayList<>();
                urlToVariantInfos.put(uri, variantInfosForUrl);
            }
            variantInfosForUrl.add(new VariantInfo(bitrate, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId));
        }
    }
    // TODO: Don't deduplicate variants by URL.
    ArrayList<Variant> deduplicatedVariants = new ArrayList<>();
    HashSet<Uri> urlsInDeduplicatedVariants = new HashSet<>();
    for (int i = 0; i < variants.size(); i++) {
        Variant variant = variants.get(i);
        if (urlsInDeduplicatedVariants.add(variant.url)) {
            Assertions.checkState(variant.format.metadata == null);
            HlsTrackMetadataEntry hlsMetadataEntry = new HlsTrackMetadataEntry(/* groupId= */
            null, /* name= */
            null, Assertions.checkNotNull(urlToVariantInfos.get(variant.url)));
            deduplicatedVariants.add(variant.copyWithFormat(variant.format.copyWithMetadata(new Metadata(hlsMetadataEntry))));
        }
    }
    for (int i = 0; i < mediaTags.size(); i++) {
        line = mediaTags.get(i);
        String groupId = parseStringAttr(line, REGEX_GROUP_ID, variableDefinitions);
        String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
        String referenceUri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
        Uri uri = referenceUri == null ? null : UriUtil.resolveToUri(baseUri, referenceUri);
        String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions);
        @C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
        @C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
        String formatId = groupId + ":" + name;
        Format format;
        Metadata metadata = new Metadata(new HlsTrackMetadataEntry(groupId, name, Collections.emptyList()));
        switch(parseStringAttr(line, REGEX_TYPE, variableDefinitions)) {
            case TYPE_VIDEO:
                Variant variant = getVariantWithVideoGroup(variants, groupId);
                String codecs = null;
                int width = Format.NO_VALUE;
                int height = Format.NO_VALUE;
                float frameRate = Format.NO_VALUE;
                if (variant != null) {
                    Format variantFormat = variant.format;
                    codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
                    width = variantFormat.width;
                    height = variantFormat.height;
                    frameRate = variantFormat.frameRate;
                }
                String sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null;
                format = Format.createVideoContainerFormat(/* id= */
                formatId, /* label= */
                name, /* containerMimeType= */
                MimeTypes.APPLICATION_M3U8, sampleMimeType, codecs, /* metadata= */
                null, /* bitrate= */
                Format.NO_VALUE, width, height, frameRate, /* initializationData= */
                null, selectionFlags, roleFlags).copyWithMetadata(metadata);
                if (uri == null) {
                // TODO: Remove this case and add a Rendition with a null uri to videos.
                } else {
                    videos.add(new Rendition(uri, format, groupId, name));
                }
                break;
            case TYPE_AUDIO:
                variant = getVariantWithAudioGroup(variants, groupId);
                codecs = variant != null ? Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_AUDIO) : null;
                sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null;
                String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
                int channelCount = Format.NO_VALUE;
                if (channelsString != null) {
                    channelCount = Integer.parseInt(Util.splitAtFirst(channelsString, "/")[0]);
                    if (MimeTypes.AUDIO_E_AC3.equals(sampleMimeType) && channelsString.endsWith("/JOC")) {
                        sampleMimeType = MimeTypes.AUDIO_E_AC3_JOC;
                    }
                }
                format = Format.createAudioContainerFormat(/* id= */
                formatId, /* label= */
                name, /* containerMimeType= */
                MimeTypes.APPLICATION_M3U8, sampleMimeType, codecs, /* metadata= */
                null, /* bitrate= */
                Format.NO_VALUE, channelCount, /* sampleRate= */
                Format.NO_VALUE, /* initializationData= */
                null, selectionFlags, roleFlags, language);
                if (uri == null) {
                    // TODO: Remove muxedAudioFormat and add a Rendition with a null uri to audios.
                    muxedAudioFormat = format;
                } else {
                    audios.add(new Rendition(uri, format.copyWithMetadata(metadata), groupId, name));
                }
                break;
            case TYPE_SUBTITLES:
                codecs = null;
                sampleMimeType = null;
                variant = getVariantWithSubtitleGroup(variants, groupId);
                if (variant != null) {
                    codecs = Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_TEXT);
                    sampleMimeType = MimeTypes.getMediaMimeType(codecs);
                }
                if (sampleMimeType == null) {
                    sampleMimeType = MimeTypes.TEXT_VTT;
                }
                format = Format.createTextContainerFormat(/* id= */
                formatId, /* label= */
                name, /* containerMimeType= */
                MimeTypes.APPLICATION_M3U8, sampleMimeType, codecs, /* bitrate= */
                Format.NO_VALUE, selectionFlags, roleFlags, language).copyWithMetadata(metadata);
                subtitles.add(new Rendition(uri, format, groupId, name));
                break;
            case TYPE_CLOSED_CAPTIONS:
                String instreamId = parseStringAttr(line, REGEX_INSTREAM_ID, variableDefinitions);
                String mimeType;
                int accessibilityChannel;
                if (instreamId.startsWith("CC")) {
                    mimeType = MimeTypes.APPLICATION_CEA608;
                    accessibilityChannel = Integer.parseInt(instreamId.substring(2));
                } else /* starts with SERVICE */
                {
                    mimeType = MimeTypes.APPLICATION_CEA708;
                    accessibilityChannel = Integer.parseInt(instreamId.substring(7));
                }
                if (muxedCaptionFormats == null) {
                    muxedCaptionFormats = new ArrayList<>();
                }
                muxedCaptionFormats.add(Format.createTextContainerFormat(/* id= */
                formatId, /* label= */
                name, /* containerMimeType= */
                null, /* sampleMimeType= */
                mimeType, /* codecs= */
                null, /* bitrate= */
                Format.NO_VALUE, selectionFlags, roleFlags, language, accessibilityChannel));
                // TODO: Remove muxedCaptionFormats and add a Rendition with a null uri to closedCaptions.
                break;
            default:
                // Do nothing.
                break;
        }
    }
    if (noClosedCaptions) {
        muxedCaptionFormats = Collections.emptyList();
    }
    return new HlsMasterPlaylist(baseUri, tags, deduplicatedVariants, videos, audios, subtitles, closedCaptions, muxedAudioFormat, muxedCaptionFormats, hasIndependentSegmentsTag, variableDefinitions, sessionKeyDrmInitData);
}
Also used : VariantInfo(com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry.VariantInfo) HashMap(java.util.HashMap) Rendition(com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Rendition) ArrayList(java.util.ArrayList) Metadata(com.google.android.exoplayer2.metadata.Metadata) SchemeData(com.google.android.exoplayer2.drm.DrmInitData.SchemeData) Uri(android.net.Uri) DrmInitData(com.google.android.exoplayer2.drm.DrmInitData) Format(com.google.android.exoplayer2.Format) HashSet(java.util.HashSet) ParserException(com.google.android.exoplayer2.ParserException) HlsTrackMetadataEntry(com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry) Variant(com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant)

Example 10 with Variant

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

Aggregations

Format (com.google.android.exoplayer2.Format)14 TrackGroup (com.google.android.exoplayer2.source.TrackGroup)11 ArrayList (java.util.ArrayList)10 Uri (android.net.Uri)7 Variant (com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant)5 Metadata (com.google.android.exoplayer2.metadata.Metadata)4 TrackGroupArray (com.google.android.exoplayer2.source.TrackGroupArray)4 Variant (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant)4 Test (org.junit.Test)4 Nullable (androidx.annotation.Nullable)3 DrmInitData (com.google.android.exoplayer2.drm.DrmInitData)3 SchemeData (com.google.android.exoplayer2.drm.DrmInitData.SchemeData)3 StreamKey (com.google.android.exoplayer2.offline.StreamKey)3 HlsTrackMetadataEntry (com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry)3 VariantInfo (com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry.VariantInfo)3 HlsMasterPlaylist (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist)3 HashMap (java.util.HashMap)3 HashSet (java.util.HashSet)3 EnsuresNonNull (org.checkerframework.checker.nullness.qual.EnsuresNonNull)3 ParserException (com.google.android.exoplayer2.ParserException)2