From 489e99158f18f10c64acc3a76e3b47086b478498 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 10 Mar 2015 19:04:38 +0000 Subject: [PATCH] Remove getDurationUs from the SampleExtractor interface. Set the duration on the MediaFormat instead. --- .../google/android/exoplayer/MediaFormat.java | 56 +++++++++++++------ .../exoplayer/chunk/parser/Extractor.java | 6 -- .../parser/mp4/FragmentedMp4Extractor.java | 5 -- .../chunk/parser/webm/WebmExtractor.java | 23 +++----- .../exoplayer/mp4/CommonMp4AtomParsers.java | 27 ++++----- .../exoplayer/source/DefaultSampleSource.java | 5 +- .../source/FrameworkSampleExtractor.java | 8 --- .../exoplayer/source/SampleExtractor.java | 3 - .../android/exoplayer/MediaFormatTest.java | 4 +- 9 files changed, 66 insertions(+), 71 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/MediaFormat.java b/library/src/main/java/com/google/android/exoplayer/MediaFormat.java index 5d1bf3945b..00db53fe2c 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaFormat.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaFormat.java @@ -40,6 +40,8 @@ public class MediaFormat { public final String mimeType; public final int maxInputSize; + public final long durationUs; + public final int width; public final int height; public final float pixelWidthHeightRatio; @@ -49,11 +51,11 @@ public class MediaFormat { public final int bitrate; + public final List initializationData; + private int maxWidth; private int maxHeight; - public final List initializationData; - // Lazy-initialized hashcode. private int hashCode; // Possibly-lazy-initialized framework media format. @@ -66,25 +68,38 @@ public class MediaFormat { public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, int width, int height, List initializationData) { - return createVideoFormat(mimeType, maxInputSize, width, height, 1, initializationData); + return createVideoFormat( + mimeType, maxInputSize, C.UNKNOWN_TIME_US, width, height, initializationData); } - public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, int width, - int height, float pixelWidthHeightRatio, List initializationData) { - return new MediaFormat(mimeType, maxInputSize, width, height, pixelWidthHeightRatio, NO_VALUE, - NO_VALUE, NO_VALUE, initializationData); + public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, long durationUs, + int width, int height, List initializationData) { + return createVideoFormat( + mimeType, maxInputSize, durationUs, width, height, 1, initializationData); + } + + public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, long durationUs, + int width, int height, float pixelWidthHeightRatio, List initializationData) { + return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, pixelWidthHeightRatio, + NO_VALUE, NO_VALUE, NO_VALUE, initializationData); } public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, int channelCount, int sampleRate, List initializationData) { - return new MediaFormat(mimeType, maxInputSize, NO_VALUE, NO_VALUE, NO_VALUE, channelCount, - sampleRate, NO_VALUE, initializationData); + return createAudioFormat( + mimeType, maxInputSize, C.UNKNOWN_TIME_US, channelCount, sampleRate, initializationData); } - public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, int channelCount, - int sampleRate, int bitrate, List initializationData) { - return new MediaFormat(mimeType, maxInputSize, NO_VALUE, NO_VALUE, NO_VALUE, channelCount, - sampleRate, bitrate, initializationData); + public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, long durationUs, + int channelCount, int sampleRate, List initializationData) { + return createAudioFormat( + mimeType, maxInputSize, durationUs, channelCount, sampleRate, NO_VALUE, initializationData); + } + + public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, long durationUs, + int channelCount, int sampleRate, int bitrate, List initializationData) { + return new MediaFormat(mimeType, maxInputSize, durationUs, NO_VALUE, NO_VALUE, NO_VALUE, + channelCount, sampleRate, bitrate, initializationData); } public static MediaFormat createId3Format() { @@ -100,8 +115,8 @@ public class MediaFormat { } public static MediaFormat createFormatForMimeType(String mimeType) { - return new MediaFormat(mimeType, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, null); + return new MediaFormat(mimeType, NO_VALUE, C.UNKNOWN_TIME_US, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, NO_VALUE, NO_VALUE, null); } @TargetApi(16) @@ -123,15 +138,18 @@ public class MediaFormat { initializationData.add(data); buffer.flip(); } + durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION) + ? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US; maxWidth = NO_VALUE; maxHeight = NO_VALUE; } - private MediaFormat(String mimeType, int maxInputSize, int width, int height, + private MediaFormat(String mimeType, int maxInputSize, long durationUs, int width, int height, float pixelWidthHeightRatio, int channelCount, int sampleRate, int bitrate, List initializationData) { this.mimeType = mimeType; this.maxInputSize = maxInputSize; + this.durationUs = durationUs; this.width = width; this.height = height; this.pixelWidthHeightRatio = pixelWidthHeightRatio; @@ -169,6 +187,7 @@ public class MediaFormat { result = 31 * result + width; result = 31 * result + height; result = 31 * result + Float.floatToRawIntBits(pixelWidthHeightRatio); + result = 31 * result + (int) durationUs; result = 31 * result + maxWidth; result = 31 * result + maxHeight; result = 31 * result + channelCount; @@ -225,7 +244,7 @@ public class MediaFormat { public String toString() { return "MediaFormat(" + mimeType + ", " + maxInputSize + ", " + width + ", " + height + ", " + pixelWidthHeightRatio + ", " + channelCount + ", " + sampleRate + ", " + bitrate + ", " - + maxWidth + ", " + maxHeight + ")"; + + durationUs + ", " + maxWidth + ", " + maxHeight + ")"; } /** @@ -246,6 +265,9 @@ public class MediaFormat { for (int i = 0; i < initializationData.size(); i++) { format.setByteBuffer("csd-" + i, ByteBuffer.wrap(initializationData.get(i))); } + if (durationUs != C.UNKNOWN_TIME_US) { + format.setLong(android.media.MediaFormat.KEY_DURATION, durationUs); + } maybeSetMaxDimensionsV16(format); frameworkMediaFormat = format; } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/parser/Extractor.java b/library/src/main/java/com/google/android/exoplayer/chunk/parser/Extractor.java index d501e26bcb..3a84099d86 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/parser/Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/parser/Extractor.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer.chunk.parser; -import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.SampleHolder; @@ -79,11 +78,6 @@ public interface Extractor { */ public MediaFormat getFormat(); - /** - * Returns the duration of the stream in microseconds, or {@link C#UNKNOWN_TIME_US} if unknown. - */ - public long getDurationUs(); - /** * Returns the pssh information parsed from the stream. * diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/parser/mp4/FragmentedMp4Extractor.java b/library/src/main/java/com/google/android/exoplayer/chunk/parser/mp4/FragmentedMp4Extractor.java index d6229cf805..da34f481e1 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/parser/mp4/FragmentedMp4Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/parser/mp4/FragmentedMp4Extractor.java @@ -198,11 +198,6 @@ public final class FragmentedMp4Extractor implements Extractor { return track == null ? null : track.mediaFormat; } - @Override - public long getDurationUs() { - return track == null ? C.UNKNOWN_TIME_US : track.durationUs; - } - @Override public int read(NonBlockingInputStream inputStream, SampleHolder out) throws ParserException { diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractor.java b/library/src/main/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractor.java index ddf15e5610..a5826984ec 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractor.java @@ -104,7 +104,7 @@ public final class WebmExtractor implements Extractor { private long segmentStartOffsetBytes = UNKNOWN; private long segmentEndOffsetBytes = UNKNOWN; private long timecodeScale = 1000000L; - private long durationUs = UNKNOWN; + private long durationUs = C.UNKNOWN_TIME_US; private int pixelWidth = UNKNOWN; private int pixelHeight = UNKNOWN; private int channelCount = UNKNOWN; @@ -181,11 +181,6 @@ public final class WebmExtractor implements Extractor { return format; } - @Override - public long getDurationUs() { - return durationUs == UNKNOWN ? C.UNKNOWN_TIME_US : durationUs; - } - @Override public Map getPsshInfo() { // TODO: Parse pssh data from Webm streams. @@ -463,8 +458,8 @@ public final class WebmExtractor implements Extractor { private void buildVideoFormat() throws ParserException { if (pixelWidth != UNKNOWN && pixelHeight != UNKNOWN && (format == null || format.width != pixelWidth || format.height != pixelHeight)) { - format = MediaFormat.createVideoFormat( - MimeTypes.VIDEO_VP9, MediaFormat.NO_VALUE, pixelWidth, pixelHeight, null); + format = MediaFormat.createVideoFormat(MimeTypes.VIDEO_VP9, MediaFormat.NO_VALUE, durationUs, + pixelWidth, pixelHeight, null); readResults |= RESULT_READ_INIT; } else if (format == null) { throw new ParserException("Unable to build format"); @@ -485,17 +480,15 @@ public final class WebmExtractor implements Extractor { && (format == null || format.channelCount != channelCount || format.sampleRate != sampleRate)) { if (CODEC_ID_VORBIS.equals(codecId)) { - format = MediaFormat.createAudioFormat( - MimeTypes.AUDIO_VORBIS, VORBIS_MAX_INPUT_SIZE, - channelCount, sampleRate, parseVorbisCodecPrivate()); + format = MediaFormat.createAudioFormat(MimeTypes.AUDIO_VORBIS, VORBIS_MAX_INPUT_SIZE, + durationUs, channelCount, sampleRate, parseVorbisCodecPrivate()); } else if (CODEC_ID_OPUS.equals(codecId)) { ArrayList opusInitializationData = new ArrayList(3); opusInitializationData.add(codecPrivate); opusInitializationData.add(ByteBuffer.allocate(Long.SIZE).putLong(codecDelayNs).array()); opusInitializationData.add(ByteBuffer.allocate(Long.SIZE).putLong(seekPreRollNs).array()); - format = MediaFormat.createAudioFormat( - MimeTypes.AUDIO_OPUS, OPUS_MAX_INPUT_SIZE, channelCount, sampleRate, - opusInitializationData); + format = MediaFormat.createAudioFormat(MimeTypes.AUDIO_OPUS, OPUS_MAX_INPUT_SIZE, + durationUs, channelCount, sampleRate, opusInitializationData); } readResults |= RESULT_READ_INIT; } else if (format == null) { @@ -512,7 +505,7 @@ public final class WebmExtractor implements Extractor { private void buildCues() throws ParserException { if (segmentStartOffsetBytes == UNKNOWN) { throw new ParserException("Segment start/end offsets unknown"); - } else if (durationUs == UNKNOWN) { + } else if (durationUs == C.UNKNOWN_TIME_US) { throw new ParserException("Duration unknown"); } else if (cuesSizeBytes == UNKNOWN) { throw new ParserException("Cues size unknown"); diff --git a/library/src/main/java/com/google/android/exoplayer/mp4/CommonMp4AtomParsers.java b/library/src/main/java/com/google/android/exoplayer/mp4/CommonMp4AtomParsers.java index 4443e573c9..0f9e754204 100644 --- a/library/src/main/java/com/google/android/exoplayer/mp4/CommonMp4AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer/mp4/CommonMp4AtomParsers.java @@ -67,7 +67,7 @@ public final class CommonMp4AtomParsers { long mediaTimescale = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data); Pair sampleDescriptions = - parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data); + parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, durationUs); return new Track(id, trackType, mediaTimescale, durationUs, sampleDescriptions.first, sampleDescriptions.second); } @@ -321,7 +321,8 @@ public final class CommonMp4AtomParsers { return mdhd.readUnsignedInt(); } - private static Pair parseStsd(ParsableByteArray stsd) { + private static Pair parseStsd( + ParsableByteArray stsd, long durationUs) { stsd.setPosition(Mp4Util.FULL_ATOM_HEADER_SIZE); int numberOfEntries = stsd.readInt(); MediaFormat mediaFormat = null; @@ -334,19 +335,19 @@ public final class CommonMp4AtomParsers { if (childAtomType == Atom.TYPE_avc1 || childAtomType == Atom.TYPE_avc3 || childAtomType == Atom.TYPE_encv) { Pair avc = - parseAvcFromParent(stsd, childStartPosition, childAtomSize); + parseAvcFromParent(stsd, childStartPosition, childAtomSize, durationUs); mediaFormat = avc.first; trackEncryptionBoxes[i] = avc.second; } else if (childAtomType == Atom.TYPE_mp4a || childAtomType == Atom.TYPE_enca || childAtomType == Atom.TYPE_ac_3) { - Pair audioSampleEntry = - parseAudioSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize); + Pair audioSampleEntry = parseAudioSampleEntry(stsd, + childAtomType, childStartPosition, childAtomSize, durationUs); mediaFormat = audioSampleEntry.first; trackEncryptionBoxes[i] = audioSampleEntry.second; } else if (childAtomType == Atom.TYPE_TTML) { mediaFormat = MediaFormat.createTtmlFormat(); } else if (childAtomType == Atom.TYPE_mp4v) { - mediaFormat = parseMp4vFromParent(stsd, childStartPosition, childAtomSize); + mediaFormat = parseMp4vFromParent(stsd, childStartPosition, childAtomSize, durationUs); } stsd.setPosition(childStartPosition + childAtomSize); } @@ -355,7 +356,7 @@ public final class CommonMp4AtomParsers { /** Returns the media format for an avc1 box. */ private static Pair parseAvcFromParent(ParsableByteArray parent, - int position, int size) { + int position, int size, long durationUs) { parent.setPosition(position + Mp4Util.ATOM_HEADER_SIZE); parent.skip(24); @@ -388,7 +389,7 @@ public final class CommonMp4AtomParsers { } MediaFormat format = MediaFormat.createVideoFormat(MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE, - width, height, pixelWidthHeightRatio, initializationData); + durationUs, width, height, pixelWidthHeightRatio, initializationData); return Pair.create(format, trackEncryptionBox); } @@ -468,8 +469,8 @@ public final class CommonMp4AtomParsers { } /** Returns the media format for an mp4v box. */ - private static MediaFormat parseMp4vFromParent(ParsableByteArray parent, - int position, int size) { + private static MediaFormat parseMp4vFromParent(ParsableByteArray parent, int position, int size, + long durationUs) { parent.setPosition(position + Mp4Util.ATOM_HEADER_SIZE); parent.skip(24); @@ -492,11 +493,11 @@ public final class CommonMp4AtomParsers { } return MediaFormat.createVideoFormat( - MimeTypes.VIDEO_MP4V, MediaFormat.NO_VALUE, width, height, initializationData); + MimeTypes.VIDEO_MP4V, MediaFormat.NO_VALUE, durationUs, width, height, initializationData); } private static Pair parseAudioSampleEntry( - ParsableByteArray parent, int atomType, int position, int size) { + ParsableByteArray parent, int atomType, int position, int size, long durationUs) { parent.setPosition(position + Mp4Util.ATOM_HEADER_SIZE); parent.skip(16); int channelCount = parent.readUnsignedShort(); @@ -555,7 +556,7 @@ public final class CommonMp4AtomParsers { } MediaFormat format = MediaFormat.createAudioFormat( - mimeType, sampleSize, channelCount, sampleRate, bitrate, + mimeType, sampleSize, durationUs, channelCount, sampleRate, bitrate, initializationData == null ? null : Collections.singletonList(initializationData)); return Pair.create(format, trackEncryptionBox); } diff --git a/library/src/main/java/com/google/android/exoplayer/source/DefaultSampleSource.java b/library/src/main/java/com/google/android/exoplayer/source/DefaultSampleSource.java index 4775e72d87..2359a974f5 100644 --- a/library/src/main/java/com/google/android/exoplayer/source/DefaultSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/source/DefaultSampleSource.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer.source; import com.google.android.exoplayer.C; +import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleSource; @@ -67,8 +68,8 @@ public final class DefaultSampleSource implements SampleSource { pendingDiscontinuities = new boolean[trackCount]; trackInfos = new TrackInfo[trackCount]; for (int track = 0; track < trackCount; track++) { - String mimeType = sampleExtractor.getMediaFormat(track).mimeType; - trackInfos[track] = new TrackInfo(mimeType, sampleExtractor.getDurationUs(track)); + MediaFormat mediaFormat = sampleExtractor.getMediaFormat(track); + trackInfos[track] = new TrackInfo(mediaFormat.mimeType, mediaFormat.durationUs); } } diff --git a/library/src/main/java/com/google/android/exoplayer/source/FrameworkSampleExtractor.java b/library/src/main/java/com/google/android/exoplayer/source/FrameworkSampleExtractor.java index 1d2d8a14ae..c001668f66 100644 --- a/library/src/main/java/com/google/android/exoplayer/source/FrameworkSampleExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/source/FrameworkSampleExtractor.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer.source; -import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleSource; @@ -146,13 +145,6 @@ public final class FrameworkSampleExtractor implements SampleExtractor { return Util.SDK_INT >= 18 ? getPsshInfoV18() : null; } - @Override - public long getDurationUs(int track) { - android.media.MediaFormat format = mediaExtractor.getTrackFormat(track); - return format.containsKey(android.media.MediaFormat.KEY_DURATION) - ? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US; - } - @Override public int readSample(int track, SampleHolder sampleHolder) { int sampleTrack = mediaExtractor.getSampleTrackIndex(); diff --git a/library/src/main/java/com/google/android/exoplayer/source/SampleExtractor.java b/library/src/main/java/com/google/android/exoplayer/source/SampleExtractor.java index cc165bc387..09b9e90380 100644 --- a/library/src/main/java/com/google/android/exoplayer/source/SampleExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/source/SampleExtractor.java @@ -81,9 +81,6 @@ public interface SampleExtractor { /** Returns the DRM initialization data for {@code track}. */ Map getDrmInitData(int track); - /** Returns the duration of {@code track} in microseconds. */ - long getDurationUs(int track); - /** * Reads the next sample in the track at index {@code track} into {@code sampleHolder}, returning * {@link SampleSource#SAMPLE_READ} if it is available. diff --git a/library/src/test/java/com/google/android/exoplayer/MediaFormatTest.java b/library/src/test/java/com/google/android/exoplayer/MediaFormatTest.java index c9d50c43b0..6da52e50f9 100644 --- a/library/src/test/java/com/google/android/exoplayer/MediaFormatTest.java +++ b/library/src/test/java/com/google/android/exoplayer/MediaFormatTest.java @@ -42,9 +42,9 @@ public class MediaFormatTest extends TestCase { initData.add(initData2); testConversionToFrameworkFormatV16( - MediaFormat.createVideoFormat("video/xyz", 102400, 1280, 720, 1.5f, initData)); + MediaFormat.createVideoFormat("video/xyz", 102400, 1000L, 1280, 720, 1.5f, initData)); testConversionToFrameworkFormatV16( - MediaFormat.createAudioFormat("audio/xyz", 102400, 5, 44100, initData)); + MediaFormat.createAudioFormat("audio/xyz", 102400, 1000L, 5, 44100, initData)); } @TargetApi(16)