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
This commit is contained in:
olly 2016-08-19 11:57:46 -07:00 committed by Oliver Woodman
parent 1579b68cf6
commit 2c84f016fe
6 changed files with 145 additions and 44 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> 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);

View File

@ -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");

View File

@ -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<byte[]> 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.
*/

View File

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