From 2c84f016fe12f220dc3da248f9c462286255f6d3 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 19 Aug 2016 11:57:46 -0700 Subject: [PATCH] Parse and expose proj and st3d/stereo_mode As described in: https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=130776689 --- .../google/android/exoplayer2/FormatTest.java | 6 +- .../java/com/google/android/exoplayer2/C.java | 15 +++ .../com/google/android/exoplayer2/Format.java | 115 +++++++++++------- .../exoplayer2/extractor/mp4/Atom.java | 3 + .../exoplayer2/extractor/mp4/AtomParsers.java | 43 ++++++- .../exoplayer2/util/ParsableByteArray.java | 7 ++ 6 files changed, 145 insertions(+), 44 deletions(-) diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/FormatTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/FormatTest.java index ebd6011b88..9bdf330b02 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/FormatTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/FormatTest.java @@ -55,10 +55,12 @@ public final class FormatTest extends TestCase { DrmInitData.SchemeData DRM_DATA_2 = new DrmInitData.SchemeData(C.UUID_NIL, VIDEO_WEBM, TestUtil.buildTestData(128, 1 /* data seed */)); DrmInitData drmInitData = new DrmInitData(DRM_DATA_1, DRM_DATA_2); + byte[] projectionData = new byte[] {1, 2, 3}; Format formatToParcel = new Format("id", MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, - 1024, 2048, 1920, 1080, 24, 90, 2, 6, 44100, C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", - Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA, drmInitData); + 1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, 6, 44100, + C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA, + drmInitData); Parcel parcel = Parcel.obtain(); formatToParcel.writeToParcel(parcel, 0); diff --git a/library/src/main/java/com/google/android/exoplayer2/C.java b/library/src/main/java/com/google/android/exoplayer2/C.java index df844f7458..9c81de4c99 100644 --- a/library/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/src/main/java/com/google/android/exoplayer2/C.java @@ -363,6 +363,21 @@ public final class C { */ public static final int MSG_CUSTOM_BASE = 10000; + /** + * Indicates Monoscopic stereo layout, used with 360/3D/VR videos. + */ + public static final int STEREO_MODE_MONO = 0; + + /** + * Indicates Top-Bottom stereo layout, used with 360/3D/VR videos. + */ + public static final int STEREO_MODE_TOP_BOTTOM = 1; + + /** + * Indicates Left-Right stereo layout, used with 360/3D/VR videos. + */ + public static final int STEREO_MODE_LEFT_RIGHT = 2; + /** * Converts a time in microseconds to the corresponding time in milliseconds, preserving * {@link #TIME_UNSET} values. diff --git a/library/src/main/java/com/google/android/exoplayer2/Format.java b/library/src/main/java/com/google/android/exoplayer2/Format.java index 5d71b01ebb..1112736830 100644 --- a/library/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/src/main/java/com/google/android/exoplayer2/Format.java @@ -137,6 +137,16 @@ public final class Format implements Parcelable { * applicable. */ public final float pixelWidthHeightRatio; + /** + * The stereo layout for 360/3D/VR video, or {@link #NO_VALUE} if not applicable. Valid stereo + * modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link + * C#STEREO_MODE_LEFT_RIGHT}. + */ + public final int stereoMode; + /** + * The projection data for 360/VR video, or null if not applicable. + */ + public final byte[] projectionData; // Audio specific. @@ -196,8 +206,8 @@ public final class Format implements Parcelable { String sampleMimeType, String codecs, int bitrate, int width, int height, float frameRate, List initializationData) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, width, - height, frameRate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, - null, OFFSET_SAMPLE_RELATIVE, initializationData, null); + height, frameRate, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, initializationData, null); } public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, @@ -211,9 +221,19 @@ public final class Format implements Parcelable { int bitrate, int maxInputSize, int width, int height, float frameRate, List initializationData, int rotationDegrees, float pixelWidthHeightRatio, DrmInitData drmInitData) { + return createVideoSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, width, + height, frameRate, initializationData, rotationDegrees, pixelWidthHeightRatio, null, + NO_VALUE, drmInitData); + } + + public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, + int bitrate, int maxInputSize, int width, int height, float frameRate, + List initializationData, int rotationDegrees, float pixelWidthHeightRatio, + byte[] projectionData, int stereoMode, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height, - frameRate, rotationDegrees, pixelWidthHeightRatio, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData); + frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, NO_VALUE, + NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, initializationData, + drmInitData); } // Audio. @@ -222,8 +242,9 @@ public final class Format implements Parcelable { String sampleMimeType, String codecs, int bitrate, int channelCount, int sampleRate, List initializationData, int selectionFlags, String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, channelCount, sampleRate, NO_VALUE, NO_VALUE, - NO_VALUE, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, initializationData, null); + NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, NO_VALUE, + NO_VALUE, NO_VALUE, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, initializationData, + null); } public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, @@ -248,9 +269,9 @@ public final class Format implements Parcelable { int encoderDelay, int encoderPadding, List initializationData, DrmInitData drmInitData, int selectionFlags, String language) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, channelCount, sampleRate, pcmEncoding, encoderDelay, - encoderPadding, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, initializationData, - drmInitData); + NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, pcmEncoding, + encoderDelay, encoderPadding, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, + initializationData, drmInitData); } // Text. @@ -258,8 +279,8 @@ public final class Format implements Parcelable { public static Format createTextContainerFormat(String id, String containerMimeType, String sampleMimeType, String codecs, int bitrate, int selectionFlags, String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, - selectionFlags, language, OFFSET_SAMPLE_RELATIVE, null, null); + NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, NO_VALUE, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, null, null); } public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, @@ -272,8 +293,8 @@ public final class Format implements Parcelable { int bitrate, int selectionFlags, String language, DrmInitData drmInitData, long subsampleOffsetUs) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, - selectionFlags, language, subsampleOffsetUs, null, drmInitData); + NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, selectionFlags, language, subsampleOffsetUs, null, drmInitData); } // Image. @@ -281,8 +302,8 @@ public final class Format implements Parcelable { public static Format createImageSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, List initializationData, String language, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, language, - OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData); + NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, 0, language, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData); } // Generic. @@ -290,22 +311,23 @@ public final class Format implements Parcelable { public static Format createContainerFormat(String id, String containerMimeType, String sampleMimeType, int bitrate) { return new Format(id, containerMimeType, sampleMimeType, null, bitrate, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, - 0, null, OFFSET_SAMPLE_RELATIVE, null, null); + NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, null, null); } public static Format createSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, - OFFSET_SAMPLE_RELATIVE, null, drmInitData); + NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, null, drmInitData); } /* package */ Format(String id, String containerMimeType, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int width, int height, float frameRate, int rotationDegrees, - float pixelWidthHeightRatio, int channelCount, int sampleRate, int pcmEncoding, - int encoderDelay, int encoderPadding, int selectionFlags, String language, - long subsampleOffsetUs, List initializationData, DrmInitData drmInitData) { + float pixelWidthHeightRatio, byte[] projectionData, int stereoMode, int channelCount, + int sampleRate, int pcmEncoding, int encoderDelay, int encoderPadding, int selectionFlags, + String language, long subsampleOffsetUs, List initializationData, + DrmInitData drmInitData) { this.id = id; this.containerMimeType = containerMimeType; this.sampleMimeType = sampleMimeType; @@ -317,6 +339,8 @@ public final class Format implements Parcelable { this.frameRate = frameRate; this.rotationDegrees = rotationDegrees; this.pixelWidthHeightRatio = pixelWidthHeightRatio; + this.projectionData = projectionData; + this.stereoMode = stereoMode; this.channelCount = channelCount; this.sampleRate = sampleRate; this.pcmEncoding = pcmEncoding; @@ -342,6 +366,9 @@ public final class Format implements Parcelable { frameRate = in.readFloat(); rotationDegrees = in.readInt(); pixelWidthHeightRatio = in.readFloat(); + boolean hasProjectionData = in.readInt() != 0; + projectionData = hasProjectionData ? in.createByteArray() : null; + stereoMode = in.readInt(); channelCount = in.readInt(); sampleRate = in.readInt(); pcmEncoding = in.readInt(); @@ -360,24 +387,24 @@ public final class Format implements Parcelable { public Format copyWithMaxInputSize(int maxInputSize) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, - width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, - pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, subsampleOffsetUs, - initializationData, drmInitData); + width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, + stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, + selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData); } public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, - width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, - pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, subsampleOffsetUs, - initializationData, drmInitData); + width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, + stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, + selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData); } public Format copyWithContainerInfo(String id, int bitrate, int width, int height, int selectionFlags, String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, - width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, - pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, subsampleOffsetUs, - initializationData, drmInitData); + width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, + stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, + selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData); } public Format copyWithManifestFormatInfo(Format manifestFormat, @@ -391,23 +418,23 @@ public final class Format implements Parcelable { DrmInitData drmInitData = (preferManifestDrmInitData && manifestFormat.drmInitData != null) || this.drmInitData == null ? manifestFormat.drmInitData : this.drmInitData; return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, - height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, - pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, subsampleOffsetUs, - initializationData, drmInitData); + height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, + channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, + language, subsampleOffsetUs, initializationData, drmInitData); } public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, - width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, - pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, subsampleOffsetUs, - initializationData, drmInitData); + width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, + stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, + selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData); } public Format copyWithDrmInitData(DrmInitData drmInitData) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, - width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, - pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, subsampleOffsetUs, - initializationData, drmInitData); + width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, + stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, + selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData); } /** @@ -484,7 +511,7 @@ public final class Format implements Parcelable { if (bitrate != other.bitrate || maxInputSize != other.maxInputSize || width != other.width || height != other.height || frameRate != other.frameRate || rotationDegrees != other.rotationDegrees - || pixelWidthHeightRatio != other.pixelWidthHeightRatio + || pixelWidthHeightRatio != other.pixelWidthHeightRatio || stereoMode != other.stereoMode || channelCount != other.channelCount || sampleRate != other.sampleRate || pcmEncoding != other.pcmEncoding || encoderDelay != other.encoderDelay || encoderPadding != other.encoderPadding || subsampleOffsetUs != other.subsampleOffsetUs @@ -494,6 +521,7 @@ public final class Format implements Parcelable { || !Util.areEqual(sampleMimeType, other.sampleMimeType) || !Util.areEqual(codecs, other.codecs) || !Util.areEqual(drmInitData, other.drmInitData) + || !Arrays.equals(projectionData, other.projectionData) || initializationData.size() != other.initializationData.size()) { return false; } @@ -546,6 +574,11 @@ public final class Format implements Parcelable { dest.writeFloat(frameRate); dest.writeInt(rotationDegrees); dest.writeFloat(pixelWidthHeightRatio); + dest.writeInt(projectionData != null ? 1 : 0); + if (projectionData != null) { + dest.writeByteArray(projectionData); + } + dest.writeInt(stereoMode); dest.writeInt(channelCount); dest.writeInt(sampleRate); dest.writeInt(pcmEncoding); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java index 915f21f3ce..e93e9e3d9c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java @@ -126,6 +126,9 @@ import java.util.List; public static final int TYPE_mean = Util.getIntegerCodeForString("mean"); public static final int TYPE_name = Util.getIntegerCodeForString("name"); public static final int TYPE_data = Util.getIntegerCodeForString("data"); + public static final int TYPE_st3d = Util.getIntegerCodeForString("st3d"); + public static final int TYPE_sv3d = Util.getIntegerCodeForString("sv3d"); + public static final int TYPE_proj = Util.getIntegerCodeForString("proj"); public static final int TYPE_vp08 = Util.getIntegerCodeForString("vp08"); public static final int TYPE_vp09 = Util.getIntegerCodeForString("vp09"); public static final int TYPE_vpcC = Util.getIntegerCodeForString("vpcC"); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index ff0a67c0cb..11a25fe419 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.AvcConfig; import com.google.android.exoplayer2.video.HevcConfig; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -663,6 +664,8 @@ import java.util.List; List initializationData = null; String mimeType = null; + byte[] projectionData = null; + int stereoMode = Format.NO_VALUE; while (childPosition - position < size) { parent.setPosition(childPosition); int childStartPosition = parent.getPosition(); @@ -705,6 +708,27 @@ import java.util.List; } else if (childAtomType == Atom.TYPE_pasp) { pixelWidthHeightRatio = parsePaspFromParent(parent, childStartPosition); pixelWidthHeightRatioFromPasp = true; + } else if (childAtomType == Atom.TYPE_sv3d) { + projectionData = parseProjFromParent(parent, childStartPosition, childAtomSize); + } else if (childAtomType == Atom.TYPE_st3d) { + int version = parent.readUnsignedByte(); + parent.skipBytes(3); // Flags. + if (version == 0) { + int layout = parent.readUnsignedByte(); + switch (layout) { + case 0: + stereoMode = C.STEREO_MODE_MONO; + break; + case 1: + stereoMode = C.STEREO_MODE_TOP_BOTTOM; + break; + case 2: + stereoMode = C.STEREO_MODE_LEFT_RIGHT; + break; + default: + break; + } + } } childPosition += childAtomSize; } @@ -716,7 +740,7 @@ import java.util.List; out.format = Format.createVideoSampleFormat(Integer.toString(trackId), mimeType, null, Format.NO_VALUE, Format.NO_VALUE, width, height, Format.NO_VALUE, initializationData, - rotationDegrees, pixelWidthHeightRatio, drmInitData); + rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, drmInitData); } /** @@ -1040,6 +1064,23 @@ import java.util.List; return null; } + /** + * Parses the proj box from sv3d box, as specified by https://github.com/google/spatial-media + */ + private static byte[] parseProjFromParent(ParsableByteArray parent, int position, int size) { + int childPosition = position + Atom.HEADER_SIZE; + while (childPosition - position < size) { + parent.setPosition(childPosition); + int childAtomSize = parent.readInt(); + int childAtomType = parent.readInt(); + if (childAtomType == Atom.TYPE_proj) { + return Arrays.copyOfRange(parent.data, childPosition, childPosition + childAtomSize); + } + childPosition += childAtomSize; + } + return null; + } + /** * Parses the size of an expandable class, as specified by ISO 14496-1 subsection 8.3.3. */ diff --git a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java index 8f32e01642..231237f0c5 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java @@ -372,6 +372,13 @@ public final class ParsableByteArray { return result; } + /** + * Reads the next four bytes as a 32-bit floating point value. + */ + public float readFloat() { + return Float.intBitsToFloat(readInt()); + } + /** * Reads the next eight bytes as a 64-bit floating point value. */