Search in sources :

Example 1 with Descriptor

use of androidx.media3.exoplayer.dash.manifest.Descriptor in project media by androidx.

the class DashMediaPeriod method getGroupedAdaptationSetIndices.

/**
 * Groups adaptation sets. Two adaptations sets belong to the same group if either:
 *
 * <ul>
 *   <li>One is a trick-play adaptation set and uses a {@code
 *       http://dashif.org/guidelines/trickmode} essential or supplemental property to indicate
 *       that the other is the main adaptation set to which it corresponds.
 *   <li>The two adaptation sets are marked as safe for switching using {@code
 *       urn:mpeg:dash:adaptation-set-switching:2016} supplemental properties.
 * </ul>
 *
 * @param adaptationSets The adaptation sets to merge.
 * @return An array of groups, where each group is an array of adaptation set indices.
 */
private static int[][] getGroupedAdaptationSetIndices(List<AdaptationSet> adaptationSets) {
    int adaptationSetCount = adaptationSets.size();
    SparseIntArray adaptationSetIdToIndex = new SparseIntArray(adaptationSetCount);
    List<List<Integer>> adaptationSetGroupedIndices = new ArrayList<>(adaptationSetCount);
    SparseArray<List<Integer>> adaptationSetIndexToGroupedIndices = new SparseArray<>(adaptationSetCount);
    // adaptationSetIdToIndex map.
    for (int i = 0; i < adaptationSetCount; i++) {
        adaptationSetIdToIndex.put(adaptationSets.get(i).id, i);
        List<Integer> initialGroup = new ArrayList<>();
        initialGroup.add(i);
        adaptationSetGroupedIndices.add(initialGroup);
        adaptationSetIndexToGroupedIndices.put(i, initialGroup);
    }
    // Merge adaptation set groups.
    for (int i = 0; i < adaptationSetCount; i++) {
        int mergedGroupIndex = i;
        AdaptationSet adaptationSet = adaptationSets.get(i);
        // Trick-play adaptation sets are merged with their corresponding main adaptation sets.
        @Nullable Descriptor trickPlayProperty = findTrickPlayProperty(adaptationSet.essentialProperties);
        if (trickPlayProperty == null) {
            // Trick-play can also be specified using a supplemental property.
            trickPlayProperty = findTrickPlayProperty(adaptationSet.supplementalProperties);
        }
        if (trickPlayProperty != null) {
            int mainAdaptationSetId = Integer.parseInt(trickPlayProperty.value);
            int mainAdaptationSetIndex = adaptationSetIdToIndex.get(mainAdaptationSetId, /* valueIfKeyNotFound= */
            -1);
            if (mainAdaptationSetIndex != -1) {
                mergedGroupIndex = mainAdaptationSetIndex;
            }
        }
        // merged group.
        if (mergedGroupIndex == i) {
            @Nullable Descriptor adaptationSetSwitchingProperty = findAdaptationSetSwitchingProperty(adaptationSet.supplementalProperties);
            if (adaptationSetSwitchingProperty != null) {
                String[] otherAdaptationSetIds = Util.split(adaptationSetSwitchingProperty.value, ",");
                for (String adaptationSetId : otherAdaptationSetIds) {
                    int otherAdaptationSetId = adaptationSetIdToIndex.get(Integer.parseInt(adaptationSetId), /* valueIfKeyNotFound= */
                    -1);
                    if (otherAdaptationSetId != -1) {
                        mergedGroupIndex = min(mergedGroupIndex, otherAdaptationSetId);
                    }
                }
            }
        }
        // Merge the groups if necessary.
        if (mergedGroupIndex != i) {
            List<Integer> thisGroup = adaptationSetIndexToGroupedIndices.get(i);
            List<Integer> mergedGroup = adaptationSetIndexToGroupedIndices.get(mergedGroupIndex);
            mergedGroup.addAll(thisGroup);
            adaptationSetIndexToGroupedIndices.put(i, mergedGroup);
            adaptationSetGroupedIndices.remove(thisGroup);
        }
    }
    int[][] groupedAdaptationSetIndices = new int[adaptationSetGroupedIndices.size()][];
    for (int i = 0; i < groupedAdaptationSetIndices.length; i++) {
        groupedAdaptationSetIndices[i] = Ints.toArray(adaptationSetGroupedIndices.get(i));
        // Restore the original adaptation set order within each group.
        Arrays.sort(groupedAdaptationSetIndices[i]);
    }
    return groupedAdaptationSetIndices;
}
Also used : ArrayList(java.util.ArrayList) SparseArray(android.util.SparseArray) SparseIntArray(android.util.SparseIntArray) Descriptor(androidx.media3.exoplayer.dash.manifest.Descriptor) List(java.util.List) ArrayList(java.util.ArrayList) AdaptationSet(androidx.media3.exoplayer.dash.manifest.AdaptationSet) Nullable(androidx.annotation.Nullable)

Example 2 with Descriptor

use of androidx.media3.exoplayer.dash.manifest.Descriptor in project media by androidx.

the class DashMediaPeriod method parseClosedCaptionDescriptor.

private static Format[] parseClosedCaptionDescriptor(Descriptor descriptor, Pattern serviceDescriptorRegex, Format baseFormat) {
    @Nullable String value = descriptor.value;
    if (value == null) {
        // There are embedded closed caption tracks, but service information is not declared.
        return new Format[] { baseFormat };
    }
    String[] services = Util.split(value, ";");
    Format[] formats = new Format[services.length];
    for (int i = 0; i < services.length; i++) {
        Matcher matcher = serviceDescriptorRegex.matcher(services[i]);
        if (!matcher.matches()) {
            // If we can't parse service information for all services, assume a single track.
            return new Format[] { baseFormat };
        }
        int accessibilityChannel = Integer.parseInt(matcher.group(1));
        formats[i] = baseFormat.buildUpon().setId(baseFormat.id + ":" + accessibilityChannel).setAccessibilityChannel(accessibilityChannel).setLanguage(matcher.group(2)).build();
    }
    return formats;
}
Also used : Format(androidx.media3.common.Format) Matcher(java.util.regex.Matcher) Nullable(androidx.annotation.Nullable)

Example 3 with Descriptor

use of androidx.media3.exoplayer.dash.manifest.Descriptor in project media by androidx.

the class DashMediaPeriod method getClosedCaptionTrackFormats.

private static Format[] getClosedCaptionTrackFormats(List<AdaptationSet> adaptationSets, int[] adaptationSetIndices) {
    for (int i : adaptationSetIndices) {
        AdaptationSet adaptationSet = adaptationSets.get(i);
        List<Descriptor> descriptors = adaptationSets.get(i).accessibilityDescriptors;
        for (int j = 0; j < descriptors.size(); j++) {
            Descriptor descriptor = descriptors.get(j);
            if ("urn:scte:dash:cc:cea-608:2015".equals(descriptor.schemeIdUri)) {
                Format cea608Format = new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_CEA608).setId(adaptationSet.id + ":cea608").build();
                return parseClosedCaptionDescriptor(descriptor, CEA608_SERVICE_DESCRIPTOR_REGEX, cea608Format);
            } else if ("urn:scte:dash:cc:cea-708:2015".equals(descriptor.schemeIdUri)) {
                Format cea708Format = new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_CEA708).setId(adaptationSet.id + ":cea708").build();
                return parseClosedCaptionDescriptor(descriptor, CEA708_SERVICE_DESCRIPTOR_REGEX, cea708Format);
            }
        }
    }
    return new Format[0];
}
Also used : Format(androidx.media3.common.Format) Descriptor(androidx.media3.exoplayer.dash.manifest.Descriptor) AdaptationSet(androidx.media3.exoplayer.dash.manifest.AdaptationSet)

Example 4 with Descriptor

use of androidx.media3.exoplayer.dash.manifest.Descriptor in project media by androidx.

the class DashManifestParser method parseRepresentation.

// Representation parsing.
protected RepresentationInfo parseRepresentation(XmlPullParser xpp, List<BaseUrl> parentBaseUrls, @Nullable String adaptationSetMimeType, @Nullable String adaptationSetCodecs, int adaptationSetWidth, int adaptationSetHeight, float adaptationSetFrameRate, int adaptationSetAudioChannels, int adaptationSetAudioSamplingRate, @Nullable String adaptationSetLanguage, List<Descriptor> adaptationSetRoleDescriptors, List<Descriptor> adaptationSetAccessibilityDescriptors, List<Descriptor> adaptationSetEssentialProperties, List<Descriptor> adaptationSetSupplementalProperties, @Nullable SegmentBase segmentBase, long periodStartUnixTimeMs, long periodDurationMs, long baseUrlAvailabilityTimeOffsetUs, long segmentBaseAvailabilityTimeOffsetUs, long timeShiftBufferDepthMs, boolean dvbProfileDeclared) throws XmlPullParserException, IOException {
    String id = xpp.getAttributeValue(null, "id");
    int bandwidth = parseInt(xpp, "bandwidth", Format.NO_VALUE);
    String mimeType = parseString(xpp, "mimeType", adaptationSetMimeType);
    String codecs = parseString(xpp, "codecs", adaptationSetCodecs);
    int width = parseInt(xpp, "width", adaptationSetWidth);
    int height = parseInt(xpp, "height", adaptationSetHeight);
    float frameRate = parseFrameRate(xpp, adaptationSetFrameRate);
    int audioChannels = adaptationSetAudioChannels;
    int audioSamplingRate = parseInt(xpp, "audioSamplingRate", adaptationSetAudioSamplingRate);
    String drmSchemeType = null;
    ArrayList<SchemeData> drmSchemeDatas = new ArrayList<>();
    ArrayList<Descriptor> inbandEventStreams = new ArrayList<>();
    ArrayList<Descriptor> essentialProperties = new ArrayList<>(adaptationSetEssentialProperties);
    ArrayList<Descriptor> supplementalProperties = new ArrayList<>(adaptationSetSupplementalProperties);
    ArrayList<BaseUrl> baseUrls = new ArrayList<>();
    boolean seenFirstBaseUrl = false;
    do {
        xpp.next();
        if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
            if (!seenFirstBaseUrl) {
                baseUrlAvailabilityTimeOffsetUs = parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
                seenFirstBaseUrl = true;
            }
            baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls, dvbProfileDeclared));
        } else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
            audioChannels = parseAudioChannelConfiguration(xpp);
        } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
            segmentBase = parseSegmentBase(xpp, (SingleSegmentBase) segmentBase);
        } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
            segmentBaseAvailabilityTimeOffsetUs = parseAvailabilityTimeOffsetUs(xpp, segmentBaseAvailabilityTimeOffsetUs);
            segmentBase = parseSegmentList(xpp, (SegmentList) segmentBase, periodStartUnixTimeMs, periodDurationMs, baseUrlAvailabilityTimeOffsetUs, segmentBaseAvailabilityTimeOffsetUs, timeShiftBufferDepthMs);
        } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
            segmentBaseAvailabilityTimeOffsetUs = parseAvailabilityTimeOffsetUs(xpp, segmentBaseAvailabilityTimeOffsetUs);
            segmentBase = parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase, adaptationSetSupplementalProperties, periodStartUnixTimeMs, periodDurationMs, baseUrlAvailabilityTimeOffsetUs, segmentBaseAvailabilityTimeOffsetUs, timeShiftBufferDepthMs);
        } else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
            Pair<String, SchemeData> contentProtection = parseContentProtection(xpp);
            if (contentProtection.first != null) {
                drmSchemeType = contentProtection.first;
            }
            if (contentProtection.second != null) {
                drmSchemeDatas.add(contentProtection.second);
            }
        } else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) {
            inbandEventStreams.add(parseDescriptor(xpp, "InbandEventStream"));
        } else if (XmlPullParserUtil.isStartTag(xpp, "EssentialProperty")) {
            essentialProperties.add(parseDescriptor(xpp, "EssentialProperty"));
        } else if (XmlPullParserUtil.isStartTag(xpp, "SupplementalProperty")) {
            supplementalProperties.add(parseDescriptor(xpp, "SupplementalProperty"));
        } else {
            maybeSkipTag(xpp);
        }
    } while (!XmlPullParserUtil.isEndTag(xpp, "Representation"));
    Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels, audioSamplingRate, bandwidth, adaptationSetLanguage, adaptationSetRoleDescriptors, adaptationSetAccessibilityDescriptors, codecs, essentialProperties, supplementalProperties);
    segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase();
    return new RepresentationInfo(format, !baseUrls.isEmpty() ? baseUrls : parentBaseUrls, segmentBase, drmSchemeType, drmSchemeDatas, inbandEventStreams, essentialProperties, supplementalProperties, Representation.REVISION_ID_DEFAULT);
}
Also used : SingleSegmentBase(androidx.media3.exoplayer.dash.manifest.SegmentBase.SingleSegmentBase) ArrayList(java.util.ArrayList) SchemeData(androidx.media3.common.DrmInitData.SchemeData) SegmentTemplate(androidx.media3.exoplayer.dash.manifest.SegmentBase.SegmentTemplate) Format(androidx.media3.common.Format) Pair(android.util.Pair)

Example 5 with Descriptor

use of androidx.media3.exoplayer.dash.manifest.Descriptor in project media by androidx.

the class DashManifestParser method parsePeriod.

protected Pair<Period, Long> parsePeriod(XmlPullParser xpp, List<BaseUrl> parentBaseUrls, long defaultStartMs, long baseUrlAvailabilityTimeOffsetUs, long availabilityStartTimeMs, long timeShiftBufferDepthMs, boolean dvbProfileDeclared) throws XmlPullParserException, IOException {
    @Nullable String id = xpp.getAttributeValue(null, "id");
    long startMs = parseDuration(xpp, "start", defaultStartMs);
    long periodStartUnixTimeMs = availabilityStartTimeMs != C.TIME_UNSET ? availabilityStartTimeMs + startMs : C.TIME_UNSET;
    long durationMs = parseDuration(xpp, "duration", C.TIME_UNSET);
    @Nullable SegmentBase segmentBase = null;
    @Nullable Descriptor assetIdentifier = null;
    List<AdaptationSet> adaptationSets = new ArrayList<>();
    List<EventStream> eventStreams = new ArrayList<>();
    ArrayList<BaseUrl> baseUrls = new ArrayList<>();
    boolean seenFirstBaseUrl = false;
    long segmentBaseAvailabilityTimeOffsetUs = C.TIME_UNSET;
    do {
        xpp.next();
        if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
            if (!seenFirstBaseUrl) {
                baseUrlAvailabilityTimeOffsetUs = parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
                seenFirstBaseUrl = true;
            }
            baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls, dvbProfileDeclared));
        } else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) {
            adaptationSets.add(parseAdaptationSet(xpp, !baseUrls.isEmpty() ? baseUrls : parentBaseUrls, segmentBase, durationMs, baseUrlAvailabilityTimeOffsetUs, segmentBaseAvailabilityTimeOffsetUs, periodStartUnixTimeMs, timeShiftBufferDepthMs, dvbProfileDeclared));
        } else if (XmlPullParserUtil.isStartTag(xpp, "EventStream")) {
            eventStreams.add(parseEventStream(xpp));
        } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
            segmentBase = parseSegmentBase(xpp, /* parent= */
            null);
        } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
            segmentBaseAvailabilityTimeOffsetUs = parseAvailabilityTimeOffsetUs(xpp, /* parentAvailabilityTimeOffsetUs= */
            C.TIME_UNSET);
            segmentBase = parseSegmentList(xpp, /* parent= */
            null, periodStartUnixTimeMs, durationMs, baseUrlAvailabilityTimeOffsetUs, segmentBaseAvailabilityTimeOffsetUs, timeShiftBufferDepthMs);
        } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
            segmentBaseAvailabilityTimeOffsetUs = parseAvailabilityTimeOffsetUs(xpp, /* parentAvailabilityTimeOffsetUs= */
            C.TIME_UNSET);
            segmentBase = parseSegmentTemplate(xpp, /* parent= */
            null, ImmutableList.of(), periodStartUnixTimeMs, durationMs, baseUrlAvailabilityTimeOffsetUs, segmentBaseAvailabilityTimeOffsetUs, timeShiftBufferDepthMs);
        } else if (XmlPullParserUtil.isStartTag(xpp, "AssetIdentifier")) {
            assetIdentifier = parseDescriptor(xpp, "AssetIdentifier");
        } else {
            maybeSkipTag(xpp);
        }
    } while (!XmlPullParserUtil.isEndTag(xpp, "Period"));
    return Pair.create(buildPeriod(id, startMs, adaptationSets, eventStreams, assetIdentifier), durationMs);
}
Also used : ArrayList(java.util.ArrayList) SingleSegmentBase(androidx.media3.exoplayer.dash.manifest.SegmentBase.SingleSegmentBase) Nullable(androidx.annotation.Nullable)

Aggregations

Nullable (androidx.annotation.Nullable)6 Format (androidx.media3.common.Format)5 ArrayList (java.util.ArrayList)4 AdaptationSet (androidx.media3.exoplayer.dash.manifest.AdaptationSet)3 SchemeData (androidx.media3.common.DrmInitData.SchemeData)2 Descriptor (androidx.media3.exoplayer.dash.manifest.Descriptor)2 SingleSegmentBase (androidx.media3.exoplayer.dash.manifest.SegmentBase.SingleSegmentBase)2 Pair (android.util.Pair)1 SparseArray (android.util.SparseArray)1 SparseIntArray (android.util.SparseIntArray)1 DrmInitData (androidx.media3.common.DrmInitData)1 Metadata (androidx.media3.common.Metadata)1 TrackGroup (androidx.media3.common.TrackGroup)1 TrackGroupArray (androidx.media3.common.TrackGroupArray)1 ParsableByteArray (androidx.media3.common.util.ParsableByteArray)1 DashManifest (androidx.media3.exoplayer.dash.manifest.DashManifest)1 SegmentTemplate (androidx.media3.exoplayer.dash.manifest.SegmentBase.SegmentTemplate)1 List (java.util.List)1 Matcher (java.util.regex.Matcher)1 Test (org.junit.Test)1