Added two new image specific properties 'tileCountHorizontal' and 'tileCountVertical' to class Format. DashManifestParser adjusted to parse these values from an EssentialProperty tag of an Image AdaptationSet. With this change, DashManifest does not have to do any parsing inside the new getThumbnailDescriptions() method. Moreover, both classes ThumbnailDescription and ThumbnailProvider adjusted to use these two properties / naming scheme accordingly.

This commit is contained in:
Görkem Güclü 2022-12-05 21:04:22 +01:00
parent 158cf0c8ed
commit 2f8fd87fa2
5 changed files with 115 additions and 31 deletions

View File

@ -92,7 +92,7 @@ public class DefaultThumbnailProvider implements ThumbnailProvider {
if (position < thumbnailDescription.getStartTimeMs() || position > thumbnailDescription.getStartTimeMs() + thumbnailDescription.getDurationMs()) return null; if (position < thumbnailDescription.getStartTimeMs() || position > thumbnailDescription.getStartTimeMs() + thumbnailDescription.getDurationMs()) return null;
int count = thumbnailDescription.getColumns() * thumbnailDescription.getRows(); int count = thumbnailDescription.getTileCountHorizontal() * thumbnailDescription.getTileCountVertical();
int durationPerImage = (int)(thumbnailDescription.getDurationMs() / count); int durationPerImage = (int)(thumbnailDescription.getDurationMs() / count);
@ -101,12 +101,12 @@ public class DefaultThumbnailProvider implements ThumbnailProvider {
//handle special case if position == duration //handle special case if position == duration
if (imageNumberToUseWithinTile > count-1) imageNumberToUseWithinTile = count-1; if (imageNumberToUseWithinTile > count-1) imageNumberToUseWithinTile = count-1;
int intRowToUse = (int)(imageNumberToUseWithinTile / thumbnailDescription.getColumns()); int intRowToUse = (int)(imageNumberToUseWithinTile / thumbnailDescription.getTileCountHorizontal());
int intColToUse = imageNumberToUseWithinTile - intRowToUse * thumbnailDescription.getColumns(); int intColToUse = imageNumberToUseWithinTile - intRowToUse * thumbnailDescription.getTileCountHorizontal();
double thumbnailWidth = (double) thumbnailDescription.getImageWidth() / thumbnailDescription.getColumns(); double thumbnailWidth = (double) thumbnailDescription.getImageWidth() / thumbnailDescription.getTileCountHorizontal();
double thumbnailHeight = (double) thumbnailDescription.getImageHeight() / thumbnailDescription.getRows(); double thumbnailHeight = (double) thumbnailDescription.getImageHeight() / thumbnailDescription.getTileCountVertical();
int cropXLeft = (int)Math.round(intColToUse * thumbnailWidth); int cropXLeft = (int)Math.round(intColToUse * thumbnailWidth);
int cropYTop = (int)Math.round(intRowToUse * thumbnailHeight); int cropYTop = (int)Math.round(intRowToUse * thumbnailHeight);

View File

@ -115,6 +115,13 @@ import java.util.UUID;
* <ul> * <ul>
* <li>{@link #accessibilityChannel} * <li>{@link #accessibilityChannel}
* </ul> * </ul>
*
* <h2 id="image-formats">Fields relevant to image formats</h2>
*
* <ul>
* <li>{@link #tileCountHorizontal}
* <li>{@link #tileCountVertical}
* </ul>
*/ */
public final class Format implements Bundleable { public final class Format implements Bundleable {
@ -174,6 +181,11 @@ public final class Format implements Bundleable {
private int accessibilityChannel; private int accessibilityChannel;
// Image specific
private int tileCountHorizontal;
private int tileCountVertical;
// Provided by the source. // Provided by the source.
private @C.CryptoType int cryptoType; private @C.CryptoType int cryptoType;
@ -197,6 +209,9 @@ public final class Format implements Bundleable {
pcmEncoding = NO_VALUE; pcmEncoding = NO_VALUE;
// Text specific. // Text specific.
accessibilityChannel = NO_VALUE; accessibilityChannel = NO_VALUE;
// Image specific.
tileCountHorizontal = NO_VALUE;
tileCountVertical = NO_VALUE;
// Provided by the source. // Provided by the source.
cryptoType = C.CRYPTO_TYPE_NONE; cryptoType = C.CRYPTO_TYPE_NONE;
} }
@ -241,6 +256,9 @@ public final class Format implements Bundleable {
this.encoderPadding = format.encoderPadding; this.encoderPadding = format.encoderPadding;
// Text specific. // Text specific.
this.accessibilityChannel = format.accessibilityChannel; this.accessibilityChannel = format.accessibilityChannel;
// Image specific.
this.tileCountHorizontal = format.tileCountHorizontal;
this.tileCountVertical = format.tileCountVertical;
// Provided by the source. // Provided by the source.
this.cryptoType = format.cryptoType; this.cryptoType = format.cryptoType;
} }
@ -616,6 +634,30 @@ public final class Format implements Bundleable {
return this; return this;
} }
// Image specific.
/**
* Sets {@link Format#tileCountHorizontal}. The default value is {@link #NO_VALUE}.
*
* @param tileCountHorizontal The {@link Format#accessibilityChannel}.
* @return The builder.
*/
public Builder setTileCountHorizontal(int tileCountHorizontal) {
this.tileCountHorizontal = tileCountHorizontal;
return this;
}
/**
* Sets {@link Format#tileCountVertical}. The default value is {@link #NO_VALUE}.
*
* @param tileCountVertical The {@link Format#accessibilityChannel}.
* @return The builder.
*/
public Builder setTileCountVertical(int tileCountVertical) {
this.tileCountVertical = tileCountVertical;
return this;
}
// Provided by source. // Provided by source.
/** /**
@ -788,6 +830,12 @@ public final class Format implements Bundleable {
/** The Accessibility channel, or {@link #NO_VALUE} if not known or applicable. */ /** The Accessibility channel, or {@link #NO_VALUE} if not known or applicable. */
public final int accessibilityChannel; public final int accessibilityChannel;
// Image specific.
/** Thumbnail tile count horizontal and vertical, or {@link #NO_VALUE} if not known or applicable. */
public final int tileCountHorizontal;
public final int tileCountVertical;
// Provided by source. // Provided by source.
/** /**
@ -1011,6 +1059,9 @@ public final class Format implements Bundleable {
encoderPadding = builder.encoderPadding == NO_VALUE ? 0 : builder.encoderPadding; encoderPadding = builder.encoderPadding == NO_VALUE ? 0 : builder.encoderPadding;
// Text specific. // Text specific.
accessibilityChannel = builder.accessibilityChannel; accessibilityChannel = builder.accessibilityChannel;
// Image specific.
tileCountHorizontal = builder.tileCountHorizontal;
tileCountVertical = builder.tileCountVertical;
// Provided by source. // Provided by source.
if (builder.cryptoType == C.CRYPTO_TYPE_NONE && drmInitData != null) { if (builder.cryptoType == C.CRYPTO_TYPE_NONE && drmInitData != null) {
// Encrypted content cannot use CRYPTO_TYPE_NONE. // Encrypted content cannot use CRYPTO_TYPE_NONE.
@ -1257,6 +1308,9 @@ public final class Format implements Bundleable {
result = 31 * result + encoderPadding; result = 31 * result + encoderPadding;
// Text specific. // Text specific.
result = 31 * result + accessibilityChannel; result = 31 * result + accessibilityChannel;
// Image specific.
result = 31 * result + tileCountHorizontal;
result = 31 * result + tileCountVertical;
// Provided by the source. // Provided by the source.
result = 31 * result + cryptoType; result = 31 * result + cryptoType;
hashCode = result; hashCode = result;
@ -1293,6 +1347,8 @@ public final class Format implements Bundleable {
&& encoderDelay == other.encoderDelay && encoderDelay == other.encoderDelay
&& encoderPadding == other.encoderPadding && encoderPadding == other.encoderPadding
&& accessibilityChannel == other.accessibilityChannel && accessibilityChannel == other.accessibilityChannel
&& tileCountHorizontal == other.tileCountHorizontal
&& tileCountVertical == other.tileCountVertical
&& cryptoType == other.cryptoType && cryptoType == other.cryptoType
&& Float.compare(frameRate, other.frameRate) == 0 && Float.compare(frameRate, other.frameRate) == 0
&& Float.compare(pixelWidthHeightRatio, other.pixelWidthHeightRatio) == 0 && Float.compare(pixelWidthHeightRatio, other.pixelWidthHeightRatio) == 0
@ -1490,6 +1546,8 @@ public final class Format implements Bundleable {
FIELD_ENCODER_PADDING, FIELD_ENCODER_PADDING,
FIELD_ACCESSIBILITY_CHANNEL, FIELD_ACCESSIBILITY_CHANNEL,
FIELD_CRYPTO_TYPE, FIELD_CRYPTO_TYPE,
FIELD_TILE_COUNT_HORIZONTAL,
FIELD_TILE_COUNT_VERTICAL,
}) })
private @interface FieldNumber {} private @interface FieldNumber {}
@ -1523,6 +1581,8 @@ public final class Format implements Bundleable {
private static final int FIELD_ENCODER_PADDING = 27; private static final int FIELD_ENCODER_PADDING = 27;
private static final int FIELD_ACCESSIBILITY_CHANNEL = 28; private static final int FIELD_ACCESSIBILITY_CHANNEL = 28;
private static final int FIELD_CRYPTO_TYPE = 29; private static final int FIELD_CRYPTO_TYPE = 29;
private static final int FIELD_TILE_COUNT_HORIZONTAL = 30;
private static final int FIELD_TILE_COUNT_VERTICAL = 31;
@Override @Override
public Bundle toBundle() { public Bundle toBundle() {
@ -1578,6 +1638,9 @@ public final class Format implements Bundleable {
bundle.putInt(keyForField(FIELD_ENCODER_PADDING), encoderPadding); bundle.putInt(keyForField(FIELD_ENCODER_PADDING), encoderPadding);
// Text specific. // Text specific.
bundle.putInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), accessibilityChannel); bundle.putInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), accessibilityChannel);
// Image specific.
bundle.putInt(keyForField(FIELD_TILE_COUNT_HORIZONTAL), tileCountHorizontal);
bundle.putInt(keyForField(FIELD_TILE_COUNT_VERTICAL), tileCountVertical);
// Source specific. // Source specific.
bundle.putInt(keyForField(FIELD_CRYPTO_TYPE), cryptoType); bundle.putInt(keyForField(FIELD_CRYPTO_TYPE), cryptoType);
return bundle; return bundle;
@ -1652,6 +1715,11 @@ public final class Format implements Bundleable {
// Text specific. // Text specific.
.setAccessibilityChannel( .setAccessibilityChannel(
bundle.getInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), DEFAULT.accessibilityChannel)) bundle.getInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), DEFAULT.accessibilityChannel))
// Image specific.
.setTileCountHorizontal(
bundle.getInt(keyForField(FIELD_TILE_COUNT_HORIZONTAL), DEFAULT.tileCountHorizontal))
.setTileCountVertical(
bundle.getInt(keyForField(FIELD_TILE_COUNT_VERTICAL), DEFAULT.tileCountVertical))
// Source specific. // Source specific.
.setCryptoType(bundle.getInt(keyForField(FIELD_CRYPTO_TYPE), DEFAULT.cryptoType)); .setCryptoType(bundle.getInt(keyForField(FIELD_CRYPTO_TYPE), DEFAULT.cryptoType));

View File

@ -7,19 +7,19 @@ public class ThumbnailDescription {
private final String id; private final String id;
private final Uri uri; private final Uri uri;
private final int bitrate; private final int bitrate;
private final int rows; private final int tileCountHorizontal;
private final int columns; private final int tileCountVertical;
private final long startTimeMs; private final long startTimeMs;
private final long durationMs; private final long durationMs;
private final int imageWidth; // Image width (Pixel) private final int imageWidth; // Image width (Pixel)
private final int imageHeight; // Image height (Pixel) private final int imageHeight; // Image height (Pixel)
public ThumbnailDescription(String id, Uri uri, int bitrate, int rows, int columns, long startTimeMs, long durationMs, int imageWidth, int imageHeight) { public ThumbnailDescription(String id, Uri uri, int bitrate, int tileCountHorizontal, int tileCountVertical, long startTimeMs, long durationMs, int imageWidth, int imageHeight) {
this.id = id; this.id = id;
this.uri = uri; this.uri = uri;
this.bitrate = bitrate; this.bitrate = bitrate;
this.rows = rows; this.tileCountHorizontal = tileCountHorizontal;
this.columns = columns; this.tileCountVertical = tileCountVertical;
this.startTimeMs = startTimeMs; this.startTimeMs = startTimeMs;
this.durationMs = durationMs; this.durationMs = durationMs;
this.imageWidth = imageWidth; this.imageWidth = imageWidth;
@ -34,12 +34,12 @@ public class ThumbnailDescription {
return bitrate; return bitrate;
} }
public int getRows() { public int getTileCountHorizontal() {
return rows; return tileCountHorizontal;
} }
public int getColumns() { public int getTileCountVertical() {
return columns; return tileCountVertical;
} }
public long getStartTimeMs() { public long getStartTimeMs() {

View File

@ -185,22 +185,9 @@ public class DashManifest implements FilterableManifest<DashManifest> {
int bitrate = representation.format.bitrate; int bitrate = representation.format.bitrate;
int imageWidth = representation.format.width; int imageWidth = representation.format.width;
int imageHeight = representation.format.height; int imageHeight = representation.format.height;
// get size XxY, e.g. 10x20, where 10 is column count and 20 is row count // get size XxY, e.g. 10x20, where 10 is column count and 20 is row count
int rows = 1; int tileCountHorizontal = representation.format.tileCountHorizontal;
int cols = 1; int tileCountVertical = representation.format.tileCountVertical;
for (int m = 0; m < representation.essentialProperties.size(); m++) {
Descriptor descriptor = representation.essentialProperties.get(m);
if ((Ascii.equalsIgnoreCase("http://dashif.org/thumbnail_tile", descriptor.schemeIdUri) || Ascii.equalsIgnoreCase("http://dashif.org/guidelines/thumbnail_tile", descriptor.schemeIdUri)) && descriptor.value != null) {
String size = descriptor.value;
String[] sizeSplit = size.split("x");
if (sizeSplit.length != 2) {
continue;
}
cols = Integer.parseInt(sizeSplit[0]);
rows = Integer.parseInt(sizeSplit[1]);
}
}
long now = Util.getNowUnixTimeMs(C.TIME_UNSET); long now = Util.getNowUnixTimeMs(C.TIME_UNSET);
String baseUrl = castNonNull(baseUrlExclusionList.selectBaseUrl(representation.baseUrls)).url; String baseUrl = castNonNull(baseUrlExclusionList.selectBaseUrl(representation.baseUrls)).url;
@ -217,7 +204,7 @@ public class DashManifest implements FilterableManifest<DashManifest> {
RangedUri rangedUri = index.getSegmentUrl(segmentNumber); RangedUri rangedUri = index.getSegmentUrl(segmentNumber);
DataSpec dataSpec = DashUtil.buildDataSpec(representation, baseUrl, rangedUri, /* flags= */ 0); DataSpec dataSpec = DashUtil.buildDataSpec(representation, baseUrl, rangedUri, /* flags= */ 0);
Uri uri = dataSpec.uri; Uri uri = dataSpec.uri;
ThumbnailDescription thumbnailDescription = new ThumbnailDescription(id, uri, bitrate, rows, cols, Util.usToMs(segmentStartTimeUs - (dynamic ? firstStartTimeUs : 0)), Util.usToMs(segmentDurationUs), imageWidth, imageHeight); ThumbnailDescription thumbnailDescription = new ThumbnailDescription(id, uri, bitrate, tileCountHorizontal, tileCountVertical, Util.usToMs(segmentStartTimeUs - (dynamic ? firstStartTimeUs : 0)), Util.usToMs(segmentDurationUs), imageWidth, imageHeight);
thumbnailDescriptions.add(thumbnailDescription); thumbnailDescriptions.add(thumbnailDescription);
} }
} }

View File

@ -811,6 +811,8 @@ public class DashManifestParser extends DefaultHandler
roleFlags |= parseRoleFlagsFromProperties(essentialProperties); roleFlags |= parseRoleFlagsFromProperties(essentialProperties);
roleFlags |= parseRoleFlagsFromProperties(supplementalProperties); roleFlags |= parseRoleFlagsFromProperties(supplementalProperties);
Pair<Integer,Integer> tileCounts = parseTileCountFromProperties(essentialProperties);
Format.Builder formatBuilder = Format.Builder formatBuilder =
new Format.Builder() new Format.Builder()
.setId(id) .setId(id)
@ -820,7 +822,9 @@ public class DashManifestParser extends DefaultHandler
.setPeakBitrate(bitrate) .setPeakBitrate(bitrate)
.setSelectionFlags(selectionFlags) .setSelectionFlags(selectionFlags)
.setRoleFlags(roleFlags) .setRoleFlags(roleFlags)
.setLanguage(language); .setLanguage(language)
.setTileCountHorizontal(tileCounts != null ? tileCounts.first : Format.NO_VALUE)
.setTileCountVertical(tileCounts != null ? tileCounts.second : Format.NO_VALUE);
if (MimeTypes.isVideo(sampleMimeType)) { if (MimeTypes.isVideo(sampleMimeType)) {
formatBuilder.setWidth(width).setHeight(height).setFrameRate(frameRate); formatBuilder.setWidth(width).setHeight(height).setFrameRate(frameRate);
@ -1629,6 +1633,31 @@ public class DashManifestParser extends DefaultHandler
return attributeValue.split(","); return attributeValue.split(",");
} }
// Thumbnail tile information parsing
/**
* Parses given descriptors for thumbnail tile information
* @param essentialProperties List of descriptor that contain thumbnail tile information
* @return A pair of Integer values, where the first is the count of horizontal tiles
* and the second is the count of vertical tiles, or null if no thumbnail tile information is found.
*/
@Nullable
protected Pair<Integer,Integer> parseTileCountFromProperties(List<Descriptor> essentialProperties) {
for (Descriptor descriptor : essentialProperties) {
if ((Ascii.equalsIgnoreCase("http://dashif.org/thumbnail_tile", descriptor.schemeIdUri) || Ascii.equalsIgnoreCase("http://dashif.org/guidelines/thumbnail_tile", descriptor.schemeIdUri)) && descriptor.value != null) {
String size = descriptor.value;
String[] sizeSplit = size.split("x");
if (sizeSplit.length != 2) {
continue;
}
int tileCountHorizontal = Integer.parseInt(sizeSplit[0]);
int tileCountVertical = Integer.parseInt(sizeSplit[1]);
return Pair.create(tileCountHorizontal, tileCountVertical);
}
}
return null;
}
// Utility methods. // Utility methods.
/** /**