Fix rotation handling as far as is possible.

Issue: #91
This commit is contained in:
Oliver Woodman 2015-08-13 11:18:15 +01:00
parent 8db1331021
commit d3995eaa7a
12 changed files with 152 additions and 61 deletions

View File

@ -76,8 +76,10 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
}
@Override
public void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio) {
Log.d(TAG, "videoSizeChanged [" + width + ", " + height + ", " + pixelWidthHeightRatio + "]");
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio) {
Log.d(TAG, "videoSizeChanged [" + width + ", " + height + ", " + unappliedRotationDegrees
+ ", " + pixelWidthHeightRatio + "]");
}
// DemoPlayer.InfoListener

View File

@ -341,7 +341,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
}
@Override
public void onVideoSizeChanged(int width, int height, float pixelWidthAspectRatio) {
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthAspectRatio) {
shutterView.setVisibility(View.GONE);
videoFrame.setAspectRatio(
height == 0 ? 1 : (width * pixelWidthAspectRatio) / height);

View File

@ -89,7 +89,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
public interface Listener {
void onStateChanged(boolean playWhenReady, int playbackState);
void onError(Exception e);
void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio);
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio);
}
/**
@ -449,9 +450,10 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
}
@Override
public void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio) {
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio) {
for (Listener listener : listeners) {
listener.onVideoSizeChanged(width, height, pixelWidthHeightRatio);
listener.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
}
}

View File

@ -91,7 +91,7 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
public void testMaxVideoDimensions() {
DashChunkSource chunkSource = new DashChunkSource(generateVodMpd(), AdaptationSet.TYPE_VIDEO,
null, null, null);
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, null);
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, 1, null);
format = chunkSource.getWithMaxVideoDimensions(format);
assertEquals(WIDE_WIDTH, format.maxWidth);
@ -121,7 +121,7 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
Representation.newInstance(0, 0, null, 0, WIDE_VIDEO, segmentBase2);
DashChunkSource chunkSource = new DashChunkSource(null, null, representation1, representation2);
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, null);
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, 1, null);
format = chunkSource.getWithMaxVideoDimensions(format);
assertEquals(WIDE_WIDTH, format.maxWidth);

View File

@ -77,8 +77,9 @@ public final class Mp4ExtractorTest extends TestCase {
+ "000000000000000000000000000003");
/** String of hexadecimal bytes containing a tkhd payload with an unknown duration. */
private static final byte[] TKHD_PAYLOAD =
getByteArray("0000000000000000000000000000000000000000FFFFFFFF");
private static final byte[] TKHD_PAYLOAD = getByteArray(
"00000007D1F0C7BFD1F0C7BF0000000000000000FFFFFFFF00000000000000000000000000000000000100"
+ "0000000000000000000000000000010000000000000000000000000000400000000780000004380000");
/** Video frame timestamps in time units. */
private static final int[] SAMPLE_TIMESTAMPS = {0, 2, 3, 5, 6, 7};
@ -87,7 +88,7 @@ public final class Mp4ExtractorTest extends TestCase {
/** Indices of key-frames. */
private static final boolean[] SAMPLE_IS_SYNC = {true, false, false, false, true, true};
/** Indices of video frame chunk offsets. */
private static final int[] CHUNK_OFFSETS = {1080, 2000, 3000, 4000};
private static final int[] CHUNK_OFFSETS = {1200, 2120, 3120, 4120};
/** Numbers of video frames in each chunk. */
private static final int[] SAMPLES_IN_CHUNK = {2, 2, 1, 1};
/** The mdat box must be large enough to avoid reading chunk sample data out of bounds. */
@ -399,7 +400,7 @@ public final class Mp4ExtractorTest extends TestCase {
atom(Atom.TYPE_stsc, getStsc()),
atom(Atom.TYPE_stsz, getStsz()),
atom(Atom.TYPE_stco, getStco())))))),
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 1048 : 1038, !mp4vFormat)));
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 1168 : 1158, !mp4vFormat)));
}
/** Gets a valid MP4 file with audio/video tracks and without a synchronization table. */
@ -435,7 +436,7 @@ public final class Mp4ExtractorTest extends TestCase {
atom(Atom.TYPE_stsc, getStsc()),
atom(Atom.TYPE_stsz, getStsz()),
atom(Atom.TYPE_stco, getStco())))))),
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 992 : 982, !mp4vFormat)));
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 1112 : 1102, !mp4vFormat)));
}
private static Mp4Atom atom(int type, Mp4Atom... containedMp4Atoms) {

View File

@ -302,6 +302,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
int maxInputSize = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_MAX_INPUT_SIZE);
int width = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_WIDTH);
int height = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_HEIGHT);
int rotationDegrees = getOptionalIntegerV16(format, "rotation-degrees");
int channelCount = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_CHANNEL_COUNT);
int sampleRate = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_SAMPLE_RATE);
ArrayList<byte[]> initializationData = new ArrayList<>();
@ -314,9 +315,9 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
}
long durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION)
? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US;
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, MediaFormat.NO_VALUE,
channelCount, sampleRate, language, initializationData, MediaFormat.NO_VALUE,
MediaFormat.NO_VALUE);
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees,
MediaFormat.NO_VALUE, channelCount, sampleRate, language, initializationData,
MediaFormat.NO_VALUE, MediaFormat.NO_VALUE);
}
@TargetApi(16)

View File

@ -26,6 +26,7 @@ import android.media.MediaCrypto;
import android.os.Handler;
import android.os.SystemClock;
import android.view.Surface;
import android.view.TextureView;
import java.nio.ByteBuffer;
@ -59,11 +60,19 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
*
* @param width The video width in pixels.
* @param height The video height in pixels.
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
* rotation in degrees that the application should apply for the video for it to be rendered
* in the correct orientation. This value will always be zero on API levels 21 and above,
* since the renderer will apply all necessary rotations internally. On earlier API levels
* this is not possible. Applications that use {@link TextureView} can apply the rotation by
* calling {@link TextureView#setTransform}. Applications that do not expect to encounter
* rotated videos can safely ignore this parameter.
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case
* of square pixels this will be equal to 1.0. Different values are indicative of anamorphic
* content.
*/
void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio);
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio);
/**
* Invoked when a frame is rendered to a surface for the first time following that surface
@ -129,12 +138,15 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
private long droppedFrameAccumulationStartTimeMs;
private int droppedFrameCount;
private int pendingRotationDegrees;
private float pendingPixelWidthHeightRatio;
private int currentWidth;
private int currentHeight;
private int currentUnappliedRotationDegrees;
private float currentPixelWidthHeightRatio;
private float pendingPixelWidthHeightRatio;
private int lastReportedWidth;
private int lastReportedHeight;
private int lastReportedUnappliedRotationDegrees;
private float lastReportedPixelWidthHeightRatio;
/**
@ -374,6 +386,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
super.onInputFormatChanged(holder);
pendingPixelWidthHeightRatio = holder.format.pixelWidthHeightRatio == MediaFormat.NO_VALUE ? 1
: holder.format.pixelWidthHeightRatio;
pendingRotationDegrees = holder.format.rotationDegrees == MediaFormat.NO_VALUE ? 0
: holder.format.rotationDegrees;
}
/**
@ -395,6 +409,20 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
? outputFormat.getInteger(KEY_CROP_BOTTOM) - outputFormat.getInteger(KEY_CROP_TOP) + 1
: outputFormat.getInteger(android.media.MediaFormat.KEY_HEIGHT);
currentPixelWidthHeightRatio = pendingPixelWidthHeightRatio;
if (Util.SDK_INT >= 21) {
// On API level 21 and above the decoder applies the rotation when rendering to the surface.
// Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need
// to flip the width, height and pixel aspect ratio to reflect the rotation that was applied.
if (pendingRotationDegrees == 90 || pendingRotationDegrees == 270) {
int rotatedHeight = currentWidth;
currentWidth = currentHeight;
currentHeight = rotatedHeight;
currentPixelWidthHeightRatio = 1 / currentPixelWidthHeightRatio;
}
} else {
// On API level 20 and below the decoder does not apply the rotation.
currentUnappliedRotationDegrees = pendingRotationDegrees;
}
}
@Override
@ -520,22 +548,26 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
private void maybeNotifyVideoSizeChanged() {
if (eventHandler == null || eventListener == null
|| (lastReportedWidth == currentWidth && lastReportedHeight == currentHeight
&& lastReportedUnappliedRotationDegrees == currentUnappliedRotationDegrees
&& lastReportedPixelWidthHeightRatio == currentPixelWidthHeightRatio)) {
return;
}
// Make final copies to ensure the runnable reports the correct values.
final int currentWidth = this.currentWidth;
final int currentHeight = this.currentHeight;
final int currentUnappliedRotationDegrees = this.currentUnappliedRotationDegrees;
final float currentPixelWidthHeightRatio = this.currentPixelWidthHeightRatio;
eventHandler.post(new Runnable() {
@Override
public void run() {
eventListener.onVideoSizeChanged(currentWidth, currentHeight, currentPixelWidthHeightRatio);
eventListener.onVideoSizeChanged(currentWidth, currentHeight,
currentUnappliedRotationDegrees, currentPixelWidthHeightRatio);
}
});
// Update the last reported values.
lastReportedWidth = currentWidth;
lastReportedHeight = currentHeight;
lastReportedUnappliedRotationDegrees = currentUnappliedRotationDegrees;
lastReportedPixelWidthHeightRatio = currentPixelWidthHeightRatio;
}

View File

@ -44,6 +44,7 @@ public final class MediaFormat {
public final int width;
public final int height;
public final int rotationDegrees;
public final float pixelWidthHeightRatio;
public final int channelCount;
@ -64,19 +65,21 @@ public final class MediaFormat {
public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, int width,
int height, List<byte[]> initializationData) {
return createVideoFormat(
mimeType, maxInputSize, C.UNKNOWN_TIME_US, width, height, initializationData);
mimeType, maxInputSize, C.UNKNOWN_TIME_US, width, height, NO_VALUE, initializationData);
}
public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, long durationUs,
int width, int height, List<byte[]> initializationData) {
int width, int height, int rotationDegrees, List<byte[]> initializationData) {
return createVideoFormat(
mimeType, maxInputSize, durationUs, width, height, 1, initializationData);
mimeType, maxInputSize, durationUs, width, height, rotationDegrees, NO_VALUE,
initializationData);
}
public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, long durationUs,
int width, int height, float pixelWidthHeightRatio, List<byte[]> initializationData) {
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, pixelWidthHeightRatio,
NO_VALUE, NO_VALUE, null, initializationData, NO_VALUE, NO_VALUE);
int width, int height, int rotationDegrees, float pixelWidthHeightRatio,
List<byte[]> initializationData) {
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees,
pixelWidthHeightRatio, NO_VALUE, NO_VALUE, null, initializationData, NO_VALUE, NO_VALUE);
}
public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, int channelCount,
@ -88,7 +91,7 @@ public final class MediaFormat {
public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, long durationUs,
int channelCount, int sampleRate, List<byte[]> initializationData) {
return new MediaFormat(mimeType, maxInputSize, durationUs, NO_VALUE, NO_VALUE, NO_VALUE,
channelCount, sampleRate, null, initializationData, NO_VALUE, NO_VALUE);
NO_VALUE, channelCount, sampleRate, null, initializationData, NO_VALUE, NO_VALUE);
}
public static MediaFormat createTextFormat(String mimeType, String language) {
@ -97,7 +100,7 @@ public final class MediaFormat {
public static MediaFormat createTextFormat(String mimeType, String language, long durationUs) {
return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, language, null, NO_VALUE, NO_VALUE);
NO_VALUE, NO_VALUE, NO_VALUE, language, null, NO_VALUE, NO_VALUE);
}
public static MediaFormat createFormatForMimeType(String mimeType) {
@ -106,17 +109,19 @@ public final class MediaFormat {
public static MediaFormat createFormatForMimeType(String mimeType, long durationUs) {
return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, null, null, NO_VALUE, NO_VALUE);
NO_VALUE, NO_VALUE, NO_VALUE, null, null, NO_VALUE, NO_VALUE);
}
/* package */ MediaFormat(String mimeType, int maxInputSize, long durationUs, int width,
int height, float pixelWidthHeightRatio, int channelCount, int sampleRate, String language,
List<byte[]> initializationData, int maxWidth, int maxHeight) {
int height, int rotationDegrees, float pixelWidthHeightRatio, int channelCount,
int sampleRate, String language, List<byte[]> initializationData, int maxWidth,
int maxHeight) {
this.mimeType = mimeType;
this.maxInputSize = maxInputSize;
this.durationUs = durationUs;
this.width = width;
this.height = height;
this.rotationDegrees = rotationDegrees;
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
this.channelCount = channelCount;
this.sampleRate = sampleRate;
@ -128,8 +133,9 @@ public final class MediaFormat {
}
public MediaFormat copyWithMaxVideoDimension(int maxWidth, int maxHeight) {
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, pixelWidthHeightRatio,
channelCount, sampleRate, language, initializationData, maxWidth, maxHeight);
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees,
pixelWidthHeightRatio, channelCount, sampleRate, language, initializationData, maxWidth,
maxHeight);
}
/**
@ -145,6 +151,7 @@ public final class MediaFormat {
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_WIDTH, width);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_HEIGHT, height);
maybeSetIntegerV16(format, "rotation-degrees", rotationDegrees);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_WIDTH, maxWidth);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_HEIGHT, maxHeight);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_CHANNEL_COUNT, channelCount);
@ -163,8 +170,8 @@ public final class MediaFormat {
@Override
public String toString() {
return "MediaFormat(" + mimeType + ", " + maxInputSize + ", " + width + ", " + height + ", "
+ pixelWidthHeightRatio + ", " + channelCount + ", " + sampleRate + ", " + language + ", "
+ durationUs + ", " + maxWidth + ", " + maxHeight + ")";
+ rotationDegrees + ", " + pixelWidthHeightRatio + ", " + channelCount + ", " + sampleRate
+ ", " + language + ", " + durationUs + ", " + maxWidth + ", " + maxHeight + ")";
}
@Override
@ -175,6 +182,7 @@ public final class MediaFormat {
result = 31 * result + maxInputSize;
result = 31 * result + width;
result = 31 * result + height;
result = 31 * result + rotationDegrees;
result = 31 * result + Float.floatToRawIntBits(pixelWidthHeightRatio);
result = 31 * result + (int) durationUs;
result = 31 * result + maxWidth;
@ -213,6 +221,7 @@ public final class MediaFormat {
private boolean equalsInternal(MediaFormat other, boolean ignoreMaxDimensions) {
if (maxInputSize != other.maxInputSize || width != other.width || height != other.height
|| rotationDegrees != other.rotationDegrees
|| pixelWidthHeightRatio != other.pixelWidthHeightRatio
|| (!ignoreMaxDimensions && (maxWidth != other.maxWidth || maxHeight != other.maxHeight))
|| channelCount != other.channelCount || sampleRate != other.sampleRate

View File

@ -50,9 +50,8 @@ import java.util.List;
return null;
}
Pair<Integer, Long> header = parseTkhd(trak.getLeafAtomOfType(Atom.TYPE_tkhd).data);
int id = header.first;
long duration = header.second;
TkhdData tkhdData = parseTkhd(trak.getLeafAtomOfType(Atom.TYPE_tkhd).data);
long duration = tkhdData.duration;
long movieTimescale = parseMvhd(mvhd.data);
long durationUs;
if (duration == -1) {
@ -64,10 +63,10 @@ import java.util.List;
.getContainerAtomOfType(Atom.TYPE_stbl);
Pair<Long, String> mdhdData = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data);
StsdDataHolder stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, durationUs,
mdhdData.second);
StsdData stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, durationUs,
tkhdData.rotationDegrees, mdhdData.second);
return stsdData.mediaFormat == null ? null
: new Track(id, trackType, mdhdData.first, durationUs, stsdData.mediaFormat,
: new Track(tkhdData.id, trackType, mdhdData.first, durationUs, stsdData.mediaFormat,
stsdData.trackEncryptionBoxes, stsdData.nalUnitLengthFieldLength);
}
@ -268,19 +267,17 @@ import java.util.List;
/**
* Parses a tkhd atom (defined in 14496-12).
*
* @return A {@link Pair} consisting of the track id and duration (in the timescale indicated in
* the movie header box). The duration is set to -1 if the duration is unspecified.
* @return An object containing the parsed data.
*/
private static Pair<Integer, Long> parseTkhd(ParsableByteArray tkhd) {
private static TkhdData parseTkhd(ParsableByteArray tkhd) {
tkhd.setPosition(Atom.HEADER_SIZE);
int fullAtom = tkhd.readInt();
int version = Atom.parseFullAtomVersion(fullAtom);
tkhd.skipBytes(version == 0 ? 8 : 16);
int trackId = tkhd.readInt();
tkhd.skipBytes(4);
tkhd.skipBytes(4);
boolean durationUnknown = true;
int durationPosition = tkhd.getPosition();
int durationByteCount = version == 0 ? 4 : 8;
@ -298,7 +295,27 @@ import java.util.List;
duration = version == 0 ? tkhd.readUnsignedInt() : tkhd.readUnsignedLongToLong();
}
return Pair.create(trackId, duration);
tkhd.skipBytes(16);
int a00 = tkhd.readInt();
int a01 = tkhd.readInt();
tkhd.skipBytes(4);
int a10 = tkhd.readInt();
int a11 = tkhd.readInt();
int rotationDegrees;
int fixedOne = 65536;
if (a00 == 0 && a01 == fixedOne && a10 == -fixedOne && a11 == 0) {
rotationDegrees = 90;
} else if (a00 == 0 && a01 == -fixedOne && a10 == fixedOne && a11 == 0) {
rotationDegrees = 270;
} else if (a00 == -fixedOne && a01 == 0 && a10 == 0 && a11 == -fixedOne) {
rotationDegrees = 180;
} else {
// Only 0, 90, 180 and 270 are supported. Treat anything else as 0.
rotationDegrees = 0;
}
return new TkhdData(trackId, duration, rotationDegrees);
}
/**
@ -333,11 +350,16 @@ import java.util.List;
return Pair.create(timescale, language);
}
private static StsdDataHolder parseStsd(ParsableByteArray stsd, long durationUs,
/**
* Parses a stsd atom (defined in 14496-12).
*
* @return An object containing the parsed data.
*/
private static StsdData parseStsd(ParsableByteArray stsd, long durationUs, int rotationDegrees,
String language) {
stsd.setPosition(Atom.FULL_HEADER_SIZE);
int numberOfEntries = stsd.readInt();
StsdDataHolder holder = new StsdDataHolder(numberOfEntries);
StsdData out = new StsdData(numberOfEntries);
for (int i = 0; i < numberOfEntries; i++) {
int childStartPosition = stsd.getPosition();
int childAtomSize = stsd.readInt();
@ -347,25 +369,26 @@ import java.util.List;
|| childAtomType == Atom.TYPE_encv || childAtomType == Atom.TYPE_mp4v
|| childAtomType == Atom.TYPE_hvc1 || childAtomType == Atom.TYPE_hev1
|| childAtomType == Atom.TYPE_s263) {
parseVideoSampleEntry(stsd, childStartPosition, childAtomSize, durationUs, holder, i);
parseVideoSampleEntry(stsd, childStartPosition, childAtomSize, durationUs, rotationDegrees,
out, i);
} else if (childAtomType == Atom.TYPE_mp4a || childAtomType == Atom.TYPE_enca
|| childAtomType == Atom.TYPE_ac_3) {
parseAudioSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, durationUs,
holder, i);
out, i);
} else if (childAtomType == Atom.TYPE_TTML) {
holder.mediaFormat = MediaFormat.createTextFormat(MimeTypes.APPLICATION_TTML, language,
out.mediaFormat = MediaFormat.createTextFormat(MimeTypes.APPLICATION_TTML, language,
durationUs);
} else if (childAtomType == Atom.TYPE_tx3g) {
holder.mediaFormat = MediaFormat.createTextFormat(MimeTypes.APPLICATION_TX3G, language,
out.mediaFormat = MediaFormat.createTextFormat(MimeTypes.APPLICATION_TX3G, language,
durationUs);
}
stsd.setPosition(childStartPosition + childAtomSize);
}
return holder;
return out;
}
private static void parseVideoSampleEntry(ParsableByteArray parent, int position, int size,
long durationUs, StsdDataHolder out, int entryIndex) {
long durationUs, int rotationDegrees, StsdData out, int entryIndex) {
parent.setPosition(position + Atom.HEADER_SIZE);
parent.skipBytes(24);
@ -428,7 +451,7 @@ import java.util.List;
}
out.mediaFormat = MediaFormat.createVideoFormat(mimeType, MediaFormat.NO_VALUE, durationUs,
width, height, pixelWidthHeightRatio, initializationData);
width, height, rotationDegrees, pixelWidthHeightRatio, initializationData);
}
private static AvcCData parseAvcCFromParent(ParsableByteArray parent, int position) {
@ -556,7 +579,7 @@ import java.util.List;
}
private static void parseAudioSampleEntry(ParsableByteArray parent, int atomType, int position,
int size, long durationUs, StsdDataHolder out, int entryIndex) {
int size, long durationUs, StsdData out, int entryIndex) {
parent.setPosition(position + Atom.HEADER_SIZE);
parent.skipBytes(16);
int channelCount = parent.readUnsignedShort();
@ -702,23 +725,43 @@ import java.util.List;
// Prevent instantiation.
}
/**
* Holds data parsed from a tkhd atom.
*/
private static final class TkhdData {
private final int id;
private final long duration;
private final int rotationDegrees;
public TkhdData(int id, long duration, int rotationDegrees) {
this.id = id;
this.duration = duration;
this.rotationDegrees = rotationDegrees;
}
}
/**
* Holds data parsed from an stsd atom and its children.
*/
private static final class StsdDataHolder {
private static final class StsdData {
public final TrackEncryptionBox[] trackEncryptionBoxes;
public MediaFormat mediaFormat;
public int nalUnitLengthFieldLength;
public StsdDataHolder(int numberOfEntries) {
public StsdData(int numberOfEntries) {
trackEncryptionBoxes = new TrackEncryptionBox[numberOfEntries];
nalUnitLengthFieldLength = -1;
}
}
/**
* Holds data parsed from an AvcC atom.
*/
private static final class AvcCData {
public final List<byte[]> initializationData;

View File

@ -211,7 +211,7 @@ import java.util.List;
// Construct and output the format.
output.format(MediaFormat.createVideoFormat(MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE,
C.UNKNOWN_TIME_US, parsedSpsData.width, parsedSpsData.height,
C.UNKNOWN_TIME_US, parsedSpsData.width, parsedSpsData.height, 0,
parsedSpsData.pixelWidthAspectRatio, initializationData));
hasOutputFormat = true;
}

View File

@ -306,7 +306,7 @@ import java.util.Collections;
}
output.format(MediaFormat.createVideoFormat(MimeTypes.VIDEO_H265, MediaFormat.NO_VALUE,
C.UNKNOWN_TIME_US, picWidthInLumaSamples, picHeightInLumaSamples, pixelWidthHeightRatio,
C.UNKNOWN_TIME_US, picWidthInLumaSamples, picHeightInLumaSamples, 0, pixelWidthHeightRatio,
Collections.singletonList(csd)));
hasOutputFormat = true;
}

View File

@ -1127,7 +1127,7 @@ public final class WebmExtractor implements Extractor {
sampleRate, initializationData);
} else if (MimeTypes.isVideo(mimeType)) {
return MediaFormat.createVideoFormat(mimeType, maxInputSize, durationUs, pixelWidth,
pixelHeight, initializationData);
pixelHeight, 0, initializationData);
} else {
throw new ParserException("Unexpected MIME type.");
}