diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java index b1b3388260..6c55fff1db 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java @@ -26,8 +26,23 @@ import java.util.Map; * https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference */ public class AviExtractor implements Extractor { + static final long UINT_MASK = 0xffffffffL; + + static long getUInt(ByteBuffer byteBuffer) { + return byteBuffer.getInt() & UINT_MASK; + } + + @NonNull + static String toString(int tag) { + final StringBuilder sb = new StringBuilder(4); + for (int i=0;i<4;i++) { + sb.append((char)(tag & 0xff)); + tag >>=8; + } + return sb.toString(); + } + static final String TAG = "AviExtractor"; - static final int KEY_FRAME_MASK = Integer.MIN_VALUE; private static final int PEEK_BYTES = 28; private static final int STATE_READ_TRACKS = 0; @@ -39,8 +54,8 @@ public class AviExtractor implements Extractor { private static final int AVIIF_KEYFRAME = 16; - static final int RIFF = AviUtil.toInt(new byte[]{'R','I','F','F'}); - static final int AVI_ = AviUtil.toInt(new byte[]{'A','V','I',' '}); + static final int RIFF = 'R' | ('I' << 8) | ('F' << 16) | ('F' << 24); + static final int AVI_ = 'A' | ('V' << 8) | ('I' << 16) | (' ' << 24); //Stream List static final int STRL = 's' | ('t' << 8) | ('r' << 16) | ('l' << 24); //movie data box @@ -103,7 +118,7 @@ public class AviExtractor implements Extractor { if (riff != AviExtractor.RIFF) { return false; } - long reportedLen = AviUtil.getUInt(byteBuffer) + byteBuffer.position(); + long reportedLen = getUInt(byteBuffer) + byteBuffer.position(); final long inputLen = input.getLength(); if (inputLen != C.LENGTH_UNSET && inputLen != reportedLen) { Log.w(TAG, "Header length doesn't match stream length"); @@ -136,7 +151,7 @@ public class AviExtractor implements Extractor { if (riff != AviExtractor.RIFF) { return null; } - long reportedLen = AviUtil.getUInt(byteBuffer) + byteBuffer.position(); + long reportedLen = getUInt(byteBuffer) + byteBuffer.position(); final long inputLen = input.getLength(); if (inputLen != C.LENGTH_UNSET && inputLen != reportedLen) { Log.w(TAG, "Header length doesn't match stream length"); @@ -177,18 +192,15 @@ public class AviExtractor implements Extractor { if (headerList == null) { throw new IOException("AVI Header List not found"); } - final List headerChildren = headerList.getChildren(); - aviHeader = AviUtil.getBox(headerChildren, AviHeaderBox.class); + aviHeader = headerList.getChild(AviHeaderBox.class); if (aviHeader == null) { throw new IOException("AviHeader not found"); } //This is usually wrong, so it will be overwritten by video if present durationUs = aviHeader.getFrames() * (long)aviHeader.getMicroSecPerFrame(); - headerChildren.remove(aviHeader); - //headerChildren should only be Stream Lists now int streamId = 0; - for (Box box : headerChildren) { + for (Box box : headerList.getChildren()) { if (box instanceof ListBox && ((ListBox) box).getListType() == STRL) { final ListBox streamList = (ListBox) box; final List streamChildren = streamList.getChildren(); @@ -238,7 +250,6 @@ public class AviExtractor implements Extractor { builder.setChannelCount(audioFormat.getChannels()); builder.setSampleRate(audioFormat.getSamplesPerSecond()); if (audioFormat.getFormatTag() == AudioFormat.WAVE_FORMAT_PCM) { - //TODO: Determine if this is LE or BE - Most likely LE final short bps = audioFormat.getBitsPerSample(); if (bps == 8) { builder.setPcmEncoding(C.ENCODING_PCM_8BIT); @@ -268,7 +279,7 @@ public class AviExtractor implements Extractor { ByteBuffer byteBuffer = allocate(12); input.readFully(byteBuffer.array(), 0,12); final int tag = byteBuffer.getInt(); - final long size = byteBuffer.getInt() & AviUtil.UINT_MASK; + final long size = getUInt(byteBuffer); final long position = input.getPosition(); //-4 because we over read for the LIST type long nextBox = position + size - 4; @@ -330,7 +341,7 @@ public class AviExtractor implements Extractor { final AviTrack aviTrack = idTrackMap.get(id); if (aviTrack == null) { if (id != AviExtractor.REC_) { - Log.w(TAG, "Unknown Track Type: " + AviUtil.toString(id)); + Log.w(TAG, "Unknown Track Type: " + toString(id)); } indexByteBuffer.position(indexByteBuffer.position() + 12); continue; @@ -413,7 +424,7 @@ public class AviExtractor implements Extractor { } else { seekPosition.position = input.getPosition() + sampleSize; if (id != JUNK) { - Log.w(TAG, "Unknown tag=" + AviUtil.toString(id) + " pos=" + (input.getPosition() - 8) + Log.w(TAG, "Unknown tag=" + toString(id) + " pos=" + (input.getPosition() - 8) + " size=" + sampleSize + " moviEnd=" + moviEnd); } } @@ -470,7 +481,6 @@ public class AviExtractor implements Extractor { @Override public void seek(long position, long timeUs) { - Log.d("Test", "Seek: pos=" + position + " us=" + timeUs); if (position == 0) { if (moviOffset != 0) { resetFrames(); diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java index c541763bba..c6c094f903 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java @@ -12,11 +12,6 @@ public class AviHeaderBox extends ResidentBox { super(type, size, byteBuffer); } - @Override - boolean assertType() { - return simpleAssert(AVIH); - } - boolean hasIndex() { return (getFlags() & AVIF_HASINDEX) > 0; } diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviUtil.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviUtil.java deleted file mode 100644 index 039a1c8acc..0000000000 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviUtil.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.google.android.exoplayer2.extractor.avi; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.google.android.exoplayer2.extractor.ExtractorInput; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.List; - -public class AviUtil { - - static final long UINT_MASK = 0xffffffffL; - - static int toInt(byte[] bytes) { - int i = 0; - for (int b=bytes.length - 1;b>=0;b--) { - i <<=8; - i |= bytes[b]; - } - return i; - } - - static long getUInt(ByteBuffer byteBuffer) { - return byteBuffer.getInt() & UINT_MASK; - } - - static void copy(ByteBuffer source, ByteBuffer dest, int bytes) { - final int inLimit = source.limit(); - source.limit(source.position() + bytes); - dest.put(source); - source.limit(inLimit); - } - - static ByteBuffer getByteBuffer(final ByteBuffer source, final int size, - final ExtractorInput input) throws IOException { - final ByteBuffer byteBuffer = AviExtractor.allocate(size); - if (size < source.remaining()) { - copy(source, byteBuffer, size); - } else { - final int copy = source.remaining(); - copy(source, byteBuffer, copy); - int remaining = size - copy; - final int offset = byteBuffer.position() + byteBuffer.arrayOffset(); - input.readFully(byteBuffer.array(), offset, remaining, false); - } - return byteBuffer; - } - - @NonNull - static String toString(int tag) { - final StringBuilder sb = new StringBuilder(4); - for (int i=0;i<4;i++) { - sb.append((char)(tag & 0xff)); - tag >>=8; - } - return sb.toString(); - } - - @Nullable - static T getBox(List list, Class clazz) { - for (Box box : list) { - if (box.getClass() == clazz) { - return (T)box; - } - } - return null; - } -} diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/Box.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/Box.java index 3cc891bc29..c5c10731f9 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/Box.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/Box.java @@ -13,11 +13,7 @@ public class Box { } public long getSize() { - return size & AviUtil.UINT_MASK; - } - - public int getSizeInt() { - return size; + return size & AviExtractor.UINT_MASK; } public int getType() { @@ -28,8 +24,4 @@ public class Box { return getType() == expected; } - boolean assertType() { - //Generic box, nothing to assert - return true; - } } diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/BoxFactory.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/BoxFactory.java index 5dc0314ee7..d8c882ac5b 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/BoxFactory.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/BoxFactory.java @@ -1,6 +1,5 @@ package com.google.android.exoplayer2.extractor.avi; -import androidx.annotation.Nullable; import com.google.android.exoplayer2.extractor.ExtractorInput; import java.io.IOException; import java.nio.ByteBuffer; @@ -16,25 +15,6 @@ public class BoxFactory { return Arrays.binarySearch(types, type) < 0; } - @Nullable - public ResidentBox createBox(final int type, final int size, final ByteBuffer byteBuffer) { - final ByteBuffer boxBuffer = AviExtractor.allocate(size); - AviUtil.copy(byteBuffer, boxBuffer, size); - //TODO: Deal with list - switch (type) { - case AviHeaderBox.AVIH: - return new AviHeaderBox(type, size, boxBuffer); - case StreamHeaderBox.STRH: - return new StreamHeaderBox(type, size, boxBuffer); - case StreamFormatBox.STRF: - return new StreamFormatBox(type, size, boxBuffer); - case StreamDataBox.STRD: - return new StreamDataBox(type, size, boxBuffer); - default: - return null; - } - } - private ResidentBox createBoxImpl(final int type, final int size, final ByteBuffer boxBuffer) { switch (type) { case AviHeaderBox.AVIH: diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ListBox.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ListBox.java index c2ca4f343e..eedb0322ea 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ListBox.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ListBox.java @@ -1,6 +1,7 @@ package com.google.android.exoplayer2.extractor.avi; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.extractor.ExtractorInput; import java.io.IOException; import java.nio.ByteBuffer; @@ -29,26 +30,20 @@ public class ListBox extends Box { return listType; } - @Override - boolean assertType() { - return simpleAssert(LIST); - } - @NonNull public List getChildren() { return new ArrayList<>(children); } -// static List realizeChildren(final ByteBuffer byteBuffer, final BoxFactory boxFactory) { -// final List list = new ArrayList<>(); -// while (byteBuffer.hasRemaining()) { -// final int type = byteBuffer.getInt(); -// final int size = byteBuffer.getInt(); -// final ResidentBox residentBox = boxFactory.createBox(type, size, byteBuffer); -// list.add(residentBox); -// } -// return list; -// } + @Nullable + public T getChild(Class c) { + for (Box box : children) { + if (box.getClass() == c) { + return (T)box; + } + } + return null; + } /** * Assume the input is pointing to the list type diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentBox.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentBox.java index 843300b8af..5234869389 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentBox.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentBox.java @@ -24,46 +24,6 @@ public class ResidentBox extends Box { this.byteBuffer = byteBuffer; } - /** - * List is not yet populated - * @param byteBuffer - * @return - * @throws IOException - */ - @Nullable - public static T getInstance(final ByteBuffer byteBuffer, - ExtractorInput input, Class boxClass) throws IOException { - if (byteBuffer.remaining() < 8) { - //Should not happen - throw new BufferUnderflowException(); - } - final int type = byteBuffer.getInt(); - final long size = AviUtil.getUInt(byteBuffer); - if (size > MAX_RESIDENT) { - throw new BufferOverflowException(); - } - final ByteBuffer boxBuffer = AviUtil.getByteBuffer(byteBuffer, (int)size, input); - return newInstance(type, (int)size, boxBuffer, boxClass); - } - - @Nullable - private static T newInstance(int type, int size, ByteBuffer boxBuffer, - Class boxClass) { - try { - final Constructor constructor = - boxClass.getDeclaredConstructor(int.class, int.class, ByteBuffer.class); - T box = constructor.newInstance(type, size, boxBuffer); - if (!box.assertType()) { - Log.e(TAG, "Expected " + AviUtil.toString(type) + " got " + AviUtil.toString(box.getType())); - return null; - } - return box; - } catch (Exception e) { - Log.e(TAG, "Create box failed " + AviUtil.toString(type)); - return null; - } - } - /** * Returns shallow copy of this ByteBuffer with the position at 0 * @return diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java index 81f2bef8a5..e0c6cea720 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java @@ -90,7 +90,7 @@ public class StreamHeaderBox extends ResidentBox { return byteBuffer.getInt(28); } public long getLength() { - return byteBuffer.getInt(32) & AviUtil.UINT_MASK; + return byteBuffer.getInt(32) & AviExtractor.UINT_MASK; } //36 - dwSuggestedBufferSize //40 - dwQuality