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. /**