From 2f8fd87fa2b08ff01155d00d7576cb08aef3c3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCcl=C3=BC?= Date: Mon, 5 Dec 2022 21:04:22 +0100 Subject: [PATCH] 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. --- .../demo/DefaultThumbnailProvider.java | 10 +-- .../com/google/android/exoplayer2/Format.java | 68 +++++++++++++++++++ .../thumbnail/ThumbnailDescription.java | 18 ++--- .../source/dash/manifest/DashManifest.java | 19 +----- .../dash/manifest/DashManifestParser.java | 31 ++++++++- 5 files changed, 115 insertions(+), 31 deletions(-) diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DefaultThumbnailProvider.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DefaultThumbnailProvider.java index c06144a1b8..2006d68990 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DefaultThumbnailProvider.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DefaultThumbnailProvider.java @@ -92,7 +92,7 @@ public class DefaultThumbnailProvider implements ThumbnailProvider { 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); @@ -101,12 +101,12 @@ public class DefaultThumbnailProvider implements ThumbnailProvider { //handle special case if position == duration 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 thumbnailHeight = (double) thumbnailDescription.getImageHeight() / thumbnailDescription.getRows(); + double thumbnailWidth = (double) thumbnailDescription.getImageWidth() / thumbnailDescription.getTileCountHorizontal(); + double thumbnailHeight = (double) thumbnailDescription.getImageHeight() / thumbnailDescription.getTileCountVertical(); int cropXLeft = (int)Math.round(intColToUse * thumbnailWidth); int cropYTop = (int)Math.round(intRowToUse * thumbnailHeight); diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Format.java b/library/common/src/main/java/com/google/android/exoplayer2/Format.java index ea3b552e1d..2a89d4677a 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Format.java @@ -115,6 +115,13 @@ import java.util.UUID; * + * + *

Fields relevant to image formats

+ * + * */ public final class Format implements Bundleable { @@ -174,6 +181,11 @@ public final class Format implements Bundleable { private int accessibilityChannel; + // Image specific + + private int tileCountHorizontal; + private int tileCountVertical; + // Provided by the source. private @C.CryptoType int cryptoType; @@ -197,6 +209,9 @@ public final class Format implements Bundleable { pcmEncoding = NO_VALUE; // Text specific. accessibilityChannel = NO_VALUE; + // Image specific. + tileCountHorizontal = NO_VALUE; + tileCountVertical = NO_VALUE; // Provided by the source. cryptoType = C.CRYPTO_TYPE_NONE; } @@ -241,6 +256,9 @@ public final class Format implements Bundleable { this.encoderPadding = format.encoderPadding; // Text specific. this.accessibilityChannel = format.accessibilityChannel; + // Image specific. + this.tileCountHorizontal = format.tileCountHorizontal; + this.tileCountVertical = format.tileCountVertical; // Provided by the source. this.cryptoType = format.cryptoType; } @@ -616,6 +634,30 @@ public final class Format implements Bundleable { 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. /** @@ -788,6 +830,12 @@ public final class Format implements Bundleable { /** The Accessibility channel, or {@link #NO_VALUE} if not known or applicable. */ 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. /** @@ -1011,6 +1059,9 @@ public final class Format implements Bundleable { encoderPadding = builder.encoderPadding == NO_VALUE ? 0 : builder.encoderPadding; // Text specific. accessibilityChannel = builder.accessibilityChannel; + // Image specific. + tileCountHorizontal = builder.tileCountHorizontal; + tileCountVertical = builder.tileCountVertical; // Provided by source. if (builder.cryptoType == C.CRYPTO_TYPE_NONE && drmInitData != null) { // Encrypted content cannot use CRYPTO_TYPE_NONE. @@ -1257,6 +1308,9 @@ public final class Format implements Bundleable { result = 31 * result + encoderPadding; // Text specific. result = 31 * result + accessibilityChannel; + // Image specific. + result = 31 * result + tileCountHorizontal; + result = 31 * result + tileCountVertical; // Provided by the source. result = 31 * result + cryptoType; hashCode = result; @@ -1293,6 +1347,8 @@ public final class Format implements Bundleable { && encoderDelay == other.encoderDelay && encoderPadding == other.encoderPadding && accessibilityChannel == other.accessibilityChannel + && tileCountHorizontal == other.tileCountHorizontal + && tileCountVertical == other.tileCountVertical && cryptoType == other.cryptoType && Float.compare(frameRate, other.frameRate) == 0 && Float.compare(pixelWidthHeightRatio, other.pixelWidthHeightRatio) == 0 @@ -1490,6 +1546,8 @@ public final class Format implements Bundleable { FIELD_ENCODER_PADDING, FIELD_ACCESSIBILITY_CHANNEL, FIELD_CRYPTO_TYPE, + FIELD_TILE_COUNT_HORIZONTAL, + FIELD_TILE_COUNT_VERTICAL, }) 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_ACCESSIBILITY_CHANNEL = 28; 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 public Bundle toBundle() { @@ -1578,6 +1638,9 @@ public final class Format implements Bundleable { bundle.putInt(keyForField(FIELD_ENCODER_PADDING), encoderPadding); // Text specific. 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. bundle.putInt(keyForField(FIELD_CRYPTO_TYPE), cryptoType); return bundle; @@ -1652,6 +1715,11 @@ public final class Format implements Bundleable { // Text specific. .setAccessibilityChannel( 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. .setCryptoType(bundle.getInt(keyForField(FIELD_CRYPTO_TYPE), DEFAULT.cryptoType)); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/thumbnail/ThumbnailDescription.java b/library/core/src/main/java/com/google/android/exoplayer2/thumbnail/ThumbnailDescription.java index 8984689404..cfd1f6e8fe 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/thumbnail/ThumbnailDescription.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/thumbnail/ThumbnailDescription.java @@ -7,19 +7,19 @@ public class ThumbnailDescription { private final String id; private final Uri uri; private final int bitrate; - private final int rows; - private final int columns; + private final int tileCountHorizontal; + private final int tileCountVertical; private final long startTimeMs; private final long durationMs; private final int imageWidth; // Image width (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.uri = uri; this.bitrate = bitrate; - this.rows = rows; - this.columns = columns; + this.tileCountHorizontal = tileCountHorizontal; + this.tileCountVertical = tileCountVertical; this.startTimeMs = startTimeMs; this.durationMs = durationMs; this.imageWidth = imageWidth; @@ -34,12 +34,12 @@ public class ThumbnailDescription { return bitrate; } - public int getRows() { - return rows; + public int getTileCountHorizontal() { + return tileCountHorizontal; } - public int getColumns() { - return columns; + public int getTileCountVertical() { + return tileCountVertical; } public long getStartTimeMs() { diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java index 9123ea559c..e44306a17e 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java @@ -185,22 +185,9 @@ public class DashManifest implements FilterableManifest { int bitrate = representation.format.bitrate; int imageWidth = representation.format.width; int imageHeight = representation.format.height; - // get size XxY, e.g. 10x20, where 10 is column count and 20 is row count - int rows = 1; - int cols = 1; - 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]); - } - } + int tileCountHorizontal = representation.format.tileCountHorizontal; + int tileCountVertical = representation.format.tileCountVertical; long now = Util.getNowUnixTimeMs(C.TIME_UNSET); String baseUrl = castNonNull(baseUrlExclusionList.selectBaseUrl(representation.baseUrls)).url; @@ -217,7 +204,7 @@ public class DashManifest implements FilterableManifest { RangedUri rangedUri = index.getSegmentUrl(segmentNumber); DataSpec dataSpec = DashUtil.buildDataSpec(representation, baseUrl, rangedUri, /* flags= */ 0); 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); } } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index 919b1e253f..1721149cfa 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -811,6 +811,8 @@ public class DashManifestParser extends DefaultHandler roleFlags |= parseRoleFlagsFromProperties(essentialProperties); roleFlags |= parseRoleFlagsFromProperties(supplementalProperties); + Pair tileCounts = parseTileCountFromProperties(essentialProperties); + Format.Builder formatBuilder = new Format.Builder() .setId(id) @@ -820,7 +822,9 @@ public class DashManifestParser extends DefaultHandler .setPeakBitrate(bitrate) .setSelectionFlags(selectionFlags) .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)) { formatBuilder.setWidth(width).setHeight(height).setFrameRate(frameRate); @@ -1629,6 +1633,31 @@ public class DashManifestParser extends DefaultHandler 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 parseTileCountFromProperties(List 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. /**