use of com.google.android.exoplayer2.source.dash.manifest.Descriptor in project ExoPlayer by google.
the class DefaultTsPayloadReaderFactory method buildSeiReader.
/**
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link SeiReader} for
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
* {@link SeiReader} for the declared formats, or {@link #closedCaptionFormats} if the descriptor
* is not present.
*
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
* @return A {@link SeiReader} for closed caption tracks.
*/
private SeiReader buildSeiReader(EsInfo esInfo) {
if (isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS)) {
return new SeiReader(closedCaptionFormats);
}
ParsableByteArray scratchDescriptorData = new ParsableByteArray(esInfo.descriptorBytes);
List<Format> closedCaptionFormats = this.closedCaptionFormats;
while (scratchDescriptorData.bytesLeft() > 0) {
int descriptorTag = scratchDescriptorData.readUnsignedByte();
int descriptorLength = scratchDescriptorData.readUnsignedByte();
int nextDescriptorPosition = scratchDescriptorData.getPosition() + descriptorLength;
if (descriptorTag == DESCRIPTOR_TAG_CAPTION_SERVICE) {
// Note: see ATSC A/65 for detailed information about the caption service descriptor.
closedCaptionFormats = new ArrayList<>();
int numberOfServices = scratchDescriptorData.readUnsignedByte() & 0x1F;
for (int i = 0; i < numberOfServices; i++) {
String language = scratchDescriptorData.readString(3);
int captionTypeByte = scratchDescriptorData.readUnsignedByte();
boolean isDigital = (captionTypeByte & 0x80) != 0;
String mimeType;
int accessibilityChannel;
if (isDigital) {
mimeType = MimeTypes.APPLICATION_CEA708;
accessibilityChannel = captionTypeByte & 0x3F;
} else {
mimeType = MimeTypes.APPLICATION_CEA608;
accessibilityChannel = 1;
}
closedCaptionFormats.add(Format.createTextSampleFormat(null, mimeType, null, Format.NO_VALUE, 0, language, accessibilityChannel, null));
// Skip easy_reader(1), wide_aspect_ratio(1), reserved(14).
scratchDescriptorData.skipBytes(2);
}
} else {
// Unknown descriptor. Ignore.
}
scratchDescriptorData.setPosition(nextDescriptorPosition);
}
return new SeiReader(closedCaptionFormats);
}
use of com.google.android.exoplayer2.source.dash.manifest.Descriptor in project ExoPlayer by google.
the class DefaultTsPayloadReaderFactory method getClosedCaptionFormats.
/**
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link List<Format>} of {@link
* #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a {@link
* List<Format>} for the declared formats, or {@link #closedCaptionFormats} if the descriptor is
* not present.
*
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
* @return A {@link List<Format>} containing list of closed caption formats.
*/
private List<Format> getClosedCaptionFormats(EsInfo esInfo) {
if (isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS)) {
return closedCaptionFormats;
}
ParsableByteArray scratchDescriptorData = new ParsableByteArray(esInfo.descriptorBytes);
List<Format> closedCaptionFormats = this.closedCaptionFormats;
while (scratchDescriptorData.bytesLeft() > 0) {
int descriptorTag = scratchDescriptorData.readUnsignedByte();
int descriptorLength = scratchDescriptorData.readUnsignedByte();
int nextDescriptorPosition = scratchDescriptorData.getPosition() + descriptorLength;
if (descriptorTag == DESCRIPTOR_TAG_CAPTION_SERVICE) {
// Note: see ATSC A/65 for detailed information about the caption service descriptor.
closedCaptionFormats = new ArrayList<>();
int numberOfServices = scratchDescriptorData.readUnsignedByte() & 0x1F;
for (int i = 0; i < numberOfServices; i++) {
String language = scratchDescriptorData.readString(3);
int captionTypeByte = scratchDescriptorData.readUnsignedByte();
boolean isDigital = (captionTypeByte & 0x80) != 0;
String mimeType;
int accessibilityChannel;
if (isDigital) {
mimeType = MimeTypes.APPLICATION_CEA708;
accessibilityChannel = captionTypeByte & 0x3F;
} else {
mimeType = MimeTypes.APPLICATION_CEA608;
accessibilityChannel = 1;
}
// easy_reader(1), wide_aspect_ratio(1), reserved(6).
byte flags = (byte) scratchDescriptorData.readUnsignedByte();
// Skip reserved (8).
scratchDescriptorData.skipBytes(1);
@Nullable List<byte[]> initializationData = null;
// The wide_aspect_ratio flag only has meaning for CEA-708.
if (isDigital) {
boolean isWideAspectRatio = (flags & 0x40) != 0;
initializationData = CodecSpecificDataUtil.buildCea708InitializationData(isWideAspectRatio);
}
closedCaptionFormats.add(new Format.Builder().setSampleMimeType(mimeType).setLanguage(language).setAccessibilityChannel(accessibilityChannel).setInitializationData(initializationData).build());
}
} else {
// Unknown descriptor. Ignore.
}
scratchDescriptorData.setPosition(nextDescriptorPosition);
}
return closedCaptionFormats;
}
use of com.google.android.exoplayer2.source.dash.manifest.Descriptor in project ExoPlayer by google.
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;
}
use of com.google.android.exoplayer2.source.dash.manifest.Descriptor in project ExoPlayer by google.
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;
}
use of com.google.android.exoplayer2.source.dash.manifest.Descriptor in project ExoPlayer by google.
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);
}
Aggregations