mirror of
https://github.com/samsonjs/media.git
synced 2026-04-15 12:55:46 +00:00
More efficient header parsing
This commit is contained in:
parent
a9c9418591
commit
1a1dc44b84
6 changed files with 142 additions and 64 deletions
|
|
@ -1,7 +1,6 @@
|
|||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
|
|
@ -17,12 +16,10 @@ import com.google.android.exoplayer2.util.MimeTypes;
|
|||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Based on the official MicroSoft spec
|
||||
|
|
@ -46,8 +43,6 @@ public class AviExtractor implements Extractor {
|
|||
static final int AVI_ = AviUtil.toInt(new byte[]{'A','V','I',' '});
|
||||
//Stream List
|
||||
static final int STRL = 's' | ('t' << 8) | ('r' << 16) | ('l' << 24);
|
||||
//Stream CODEC data
|
||||
static final int STRD = 's' | ('t' << 8) | ('r' << 16) | ('d' << 24);
|
||||
//movie data box
|
||||
static final int MOVI = 'm' | ('o' << 8) | ('v' << 16) | ('i' << 24);
|
||||
//Index
|
||||
|
|
@ -144,20 +139,20 @@ public class AviExtractor implements Extractor {
|
|||
if (inputLen != C.LENGTH_UNSET && inputLen != reportedLen) {
|
||||
Log.w(TAG, "Header length doesn't match stream length");
|
||||
}
|
||||
int avi = byteBuffer.getInt();
|
||||
final int avi = byteBuffer.getInt();
|
||||
if (avi != AviExtractor.AVI_) {
|
||||
return null;
|
||||
}
|
||||
final ListBox header = ListBox.getInstance(byteBuffer, input, ListBox.class);
|
||||
if (header == null) {
|
||||
final int list = byteBuffer.getInt();
|
||||
if (list != ListBox.LIST) {
|
||||
return null;
|
||||
}
|
||||
if (header.getListType() != ListBox.TYPE_HDRL) {
|
||||
Log.e(TAG, "Expected " +AviUtil.toString(ListBox.TYPE_HDRL) + ", got: " +
|
||||
AviUtil.toString(header.getType()));
|
||||
final int listSize = byteBuffer.getInt();
|
||||
final ListBox listBox = ListBox.newInstance(listSize, new BoxFactory(), input);
|
||||
if (listBox.getListType() != ListBox.TYPE_HDRL) {
|
||||
return null;
|
||||
}
|
||||
return header;
|
||||
return listBox;
|
||||
}
|
||||
|
||||
long getDuration() {
|
||||
|
|
@ -173,7 +168,7 @@ public class AviExtractor implements Extractor {
|
|||
this.output = output;
|
||||
}
|
||||
|
||||
private static ResidentBox peekNext(final List<ResidentBox> streams, int i, int type) {
|
||||
private static Box peekNext(final List<Box> streams, int i, int type) {
|
||||
if (i + 1 < streams.size() && streams.get(i + 1).getType() == type) {
|
||||
return streams.get(i + 1);
|
||||
}
|
||||
|
|
@ -185,8 +180,7 @@ public class AviExtractor implements Extractor {
|
|||
if (headerList == null) {
|
||||
throw new IOException("AVI Header List not found");
|
||||
}
|
||||
final BoxFactory boxFactory = new BoxFactory();
|
||||
final List<ResidentBox> headerChildren = headerList.getBoxList(boxFactory);
|
||||
final List<Box> headerChildren = headerList.getChildren();
|
||||
aviHeader = AviUtil.getBox(headerChildren, AviHeaderBox.class);
|
||||
if (aviHeader == null) {
|
||||
throw new IOException("AviHeader not found");
|
||||
|
|
@ -198,9 +192,9 @@ public class AviExtractor implements Extractor {
|
|||
for (Box box : headerChildren) {
|
||||
if (box instanceof ListBox && ((ListBox) box).getListType() == STRL) {
|
||||
final ListBox streamList = (ListBox) box;
|
||||
final List<ResidentBox> streamChildren = streamList.getBoxList(boxFactory);
|
||||
final List<Box> streamChildren = streamList.getChildren();
|
||||
for (int i=0;i<streamChildren.size();i++) {
|
||||
final ResidentBox residentBox = streamChildren.get(i);
|
||||
final Box residentBox = streamChildren.get(i);
|
||||
if (residentBox instanceof StreamHeaderBox) {
|
||||
final StreamHeaderBox streamHeader = (StreamHeaderBox) residentBox;
|
||||
final StreamFormatBox streamFormat = (StreamFormatBox) peekNext(streamChildren, i, StreamFormatBox.STRF);
|
||||
|
|
@ -208,10 +202,10 @@ public class AviExtractor implements Extractor {
|
|||
i++;
|
||||
if (streamHeader.isVideo()) {
|
||||
final VideoFormat videoFormat = streamFormat.getVideoFormat();
|
||||
final ResidentBox codecBox = (ResidentBox) peekNext(streamChildren, i, STRD);
|
||||
final StreamDataBox codecBox = (StreamDataBox) peekNext(streamChildren, i, StreamDataBox.STRD);
|
||||
final List<byte[]> codecData;
|
||||
if (codecBox != null) {
|
||||
codecData = Collections.singletonList(codecBox.byteBuffer.array());
|
||||
codecData = Collections.singletonList(codecBox.getData());
|
||||
i++;
|
||||
} else {
|
||||
codecData = null;
|
||||
|
|
|
|||
|
|
@ -4,15 +4,19 @@ package com.google.android.exoplayer2.extractor.avi;
|
|||
* This is referred to as a Chunk in the MS spec, but that gets confusing with AV chunks
|
||||
*/
|
||||
public class Box {
|
||||
private final long size;
|
||||
private final int size;
|
||||
private final int type;
|
||||
|
||||
Box(int type, long size) {
|
||||
Box(int type, int size) {
|
||||
this.type = type;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size & AviUtil.UINT_MASK;
|
||||
}
|
||||
|
||||
public int getSizeInt() {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,60 @@
|
|||
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.Arrays;
|
||||
|
||||
public class BoxFactory {
|
||||
@NonNull
|
||||
static int[] types = {AviHeaderBox.AVIH, StreamHeaderBox.STRH, StreamFormatBox.STRF, StreamDataBox.STRD};
|
||||
static {
|
||||
Arrays.sort(types);
|
||||
}
|
||||
|
||||
public boolean isUnknown(final int type) {
|
||||
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:
|
||||
return new AviHeaderBox(type, size, boxBuffer);
|
||||
case ListBox.LIST:
|
||||
return new ListBox(type, size, boxBuffer);
|
||||
case StreamHeaderBox.STRH:
|
||||
return new StreamHeaderBox(type, size, boxBuffer);
|
||||
case StreamFormatBox.STRF:
|
||||
return new StreamFormatBox(type, size, boxBuffer);
|
||||
default:
|
||||
return new ResidentBox(type, size, boxBuffer);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ResidentBox createBox(final int type, final int size, ExtractorInput input) throws IOException {
|
||||
if (isUnknown(type)) {
|
||||
input.skipFully(size);
|
||||
return null;
|
||||
}
|
||||
final ByteBuffer boxBuffer = AviExtractor.allocate(size);
|
||||
input.readFully(boxBuffer.array(),0,size);
|
||||
return createBoxImpl(type, size, boxBuffer);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,28 @@
|
|||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An AVI LIST box, memory resident
|
||||
*/
|
||||
public class ListBox extends ResidentBox {
|
||||
public class ListBox extends Box {
|
||||
public static final int LIST = 'L' | ('I' << 8) | ('S' << 16) | ('T' << 24);
|
||||
//Header List
|
||||
public static final int TYPE_HDRL = 'h' | ('d' << 8) | ('r' << 16) | ('l' << 24);
|
||||
|
||||
private final int listType;
|
||||
|
||||
ListBox(int type, int size, ByteBuffer byteBuffer) {
|
||||
super(type, size, byteBuffer);
|
||||
listType = byteBuffer.getInt(0);
|
||||
final List<Box> children;
|
||||
|
||||
ListBox(int size, int listType, List<Box> children) {
|
||||
super(LIST, size);
|
||||
this.listType = listType;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public int getListType() {
|
||||
|
|
@ -25,4 +33,56 @@ public class ListBox extends ResidentBox {
|
|||
boolean assertType() {
|
||||
return simpleAssert(LIST);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<Box> getChildren() {
|
||||
return new ArrayList<>(children);
|
||||
}
|
||||
|
||||
// static List<ResidentBox> realizeChildren(final ByteBuffer byteBuffer, final BoxFactory boxFactory) {
|
||||
// final List<ResidentBox> 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;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Assume the input is pointing to the list type
|
||||
* @param boxFactory
|
||||
* @param input
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ListBox newInstance(final int listSize, BoxFactory boxFactory,
|
||||
ExtractorInput input) throws IOException {
|
||||
|
||||
final List<Box> list = new ArrayList<>();
|
||||
final ByteBuffer headerBuffer = AviExtractor.allocate(8);
|
||||
byte [] bytes = headerBuffer.array();
|
||||
input.readFully(bytes, 0, 4);
|
||||
final int listType = headerBuffer.getInt();
|
||||
|
||||
long endPos = input.getPosition() + listSize - 4;
|
||||
while (input.getPosition() + 8 < endPos) {
|
||||
headerBuffer.clear();
|
||||
input.readFully(bytes, 0, 8);
|
||||
final int type = headerBuffer.getInt();
|
||||
final int size = headerBuffer.getInt();
|
||||
final Box box;
|
||||
if (type == LIST) {
|
||||
box = newInstance(size, boxFactory, input);
|
||||
} else {
|
||||
box = boxFactory.createBox(type, size, input);
|
||||
}
|
||||
|
||||
if (box != null) {
|
||||
list.add(box);
|
||||
}
|
||||
}
|
||||
return new ListBox(listSize, listType, list);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,32 +10,15 @@ import java.nio.BufferOverflowException;
|
|||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A box that is resident in memory
|
||||
*/
|
||||
public class ResidentBox extends Box {
|
||||
private static final String TAG = AviExtractor.TAG;
|
||||
final private static int MAX_RESIDENT = 64*1024;
|
||||
final private static int MAX_RESIDENT = 1024;
|
||||
final ByteBuffer byteBuffer;
|
||||
|
||||
// private Class<? extends ResidentBox> getClass(final int type) {
|
||||
// switch (type) {
|
||||
// case AviHeaderBox.AVIH:
|
||||
// return AviHeaderBox.class;
|
||||
// case ListBox.LIST:
|
||||
// return ListBox.class;
|
||||
// case StreamHeaderBox.STRH:
|
||||
// return StreamHeaderBox.class;
|
||||
// case StreamFormatBox.STRF:
|
||||
// return StreamFormatBox.class;
|
||||
// default:
|
||||
// return ResidentBox.class;
|
||||
// }
|
||||
// }
|
||||
|
||||
ResidentBox(int type, int size, ByteBuffer byteBuffer) {
|
||||
super(type, size);
|
||||
this.byteBuffer = byteBuffer;
|
||||
|
|
@ -81,7 +64,6 @@ public class ResidentBox extends Box {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns shallow copy of this ByteBuffer with the position at 0
|
||||
* @return
|
||||
|
|
@ -93,18 +75,4 @@ public class ResidentBox extends Box {
|
|||
clone.clear();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<ResidentBox> getBoxList(final BoxFactory boxFactory) {
|
||||
final ByteBuffer temp = getByteBuffer();
|
||||
temp.position(4);
|
||||
final List<ResidentBox> list = new ArrayList<>();
|
||||
while (temp.hasRemaining()) {
|
||||
final int type = temp.getInt();
|
||||
final int size = temp.getInt();
|
||||
final ResidentBox residentBox = boxFactory.createBox(type, size, temp);
|
||||
list.add(residentBox);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class StreamDataBox extends ResidentBox {
|
||||
//Stream CODEC data
|
||||
static final int STRD = 's' | ('t' << 8) | ('r' << 16) | ('d' << 24);
|
||||
|
||||
StreamDataBox(int type, int size, ByteBuffer byteBuffer) {
|
||||
super(type, size, byteBuffer);
|
||||
}
|
||||
byte[] getData() {
|
||||
byte[] data = new byte[byteBuffer.capacity()];
|
||||
System.arraycopy(byteBuffer.array(), 0, data, 0, data.length);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue