Add support for StreamName fixed issues with position alignment
This commit is contained in:
parent
167c2f3fc0
commit
43b8a9b336
@ -92,6 +92,13 @@ public class AviExtractor implements Extractor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long alignPosition(long position) {
|
||||||
|
if ((position & 1) == 1) {
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
public AviExtractor() {
|
public AviExtractor() {
|
||||||
this(0);
|
this(0);
|
||||||
}
|
}
|
||||||
@ -212,70 +219,73 @@ public class AviExtractor implements Extractor {
|
|||||||
for (Box box : headerList.getChildren()) {
|
for (Box box : headerList.getChildren()) {
|
||||||
if (box instanceof ListBox && ((ListBox) box).getListType() == STRL) {
|
if (box instanceof ListBox && ((ListBox) box).getListType() == STRL) {
|
||||||
final ListBox streamList = (ListBox) box;
|
final ListBox streamList = (ListBox) box;
|
||||||
final List<Box> streamChildren = streamList.getChildren();
|
final StreamHeaderBox streamHeader = streamList.getChild(StreamHeaderBox.class);
|
||||||
for (int i=0;i<streamChildren.size();i++) {
|
final StreamFormatBox streamFormat = streamList.getChild(StreamFormatBox.class);
|
||||||
final Box residentBox = streamChildren.get(i);
|
if (streamHeader == null) {
|
||||||
if (residentBox instanceof StreamHeaderBox) {
|
Log.w(TAG, "Missing Stream Header");
|
||||||
final StreamHeaderBox streamHeader = (StreamHeaderBox) residentBox;
|
continue;
|
||||||
final StreamFormatBox streamFormat = (StreamFormatBox) peekNext(streamChildren, i, StreamFormatBox.STRF);
|
|
||||||
if (streamFormat != null) {
|
|
||||||
i++;
|
|
||||||
if (streamHeader.isVideo()) {
|
|
||||||
final String mimeType = streamHeader.getMimeType();
|
|
||||||
if (mimeType == null) {
|
|
||||||
Log.w(TAG, "Unknown FourCC: " + toString(streamHeader.getFourCC()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final VideoFormat videoFormat = streamFormat.getVideoFormat();
|
|
||||||
final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_VIDEO);
|
|
||||||
final Format.Builder builder = new Format.Builder();
|
|
||||||
builder.setWidth(videoFormat.getWidth());
|
|
||||||
builder.setHeight(videoFormat.getHeight());
|
|
||||||
builder.setFrameRate(streamHeader.getFrameRate());
|
|
||||||
builder.setSampleMimeType(mimeType);
|
|
||||||
|
|
||||||
final AviTrack aviTrack;
|
|
||||||
switch (mimeType) {
|
|
||||||
case MimeTypes.VIDEO_MP4V:
|
|
||||||
aviTrack = new Mp4vAviTrack(streamId, streamHeader, trackOutput, builder);
|
|
||||||
break;
|
|
||||||
case MimeTypes.VIDEO_H264:
|
|
||||||
aviTrack = new AvcAviTrack(streamId, streamHeader, trackOutput, builder);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
aviTrack = new AviTrack(streamId, streamHeader, trackOutput);
|
|
||||||
}
|
|
||||||
trackOutput.format(builder.build());
|
|
||||||
idTrackMap.put('0' | (('0' + streamId) << 8) | ('d' << 16) | ('c' << 24), aviTrack);
|
|
||||||
durationUs = streamHeader.getUsPerSample() * streamHeader.getLength();
|
|
||||||
} else if (streamHeader.isAudio()) {
|
|
||||||
final AudioFormat audioFormat = streamFormat.getAudioFormat();
|
|
||||||
final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_AUDIO);
|
|
||||||
final Format.Builder builder = new Format.Builder();
|
|
||||||
final String mimeType = audioFormat.getMimeType();
|
|
||||||
builder.setSampleMimeType(mimeType);
|
|
||||||
//builder.setCodecs(audioFormat.getCodec());
|
|
||||||
builder.setChannelCount(audioFormat.getChannels());
|
|
||||||
builder.setSampleRate(audioFormat.getSamplesPerSecond());
|
|
||||||
if (audioFormat.getFormatTag() == AudioFormat.WAVE_FORMAT_PCM) {
|
|
||||||
final short bps = audioFormat.getBitsPerSample();
|
|
||||||
if (bps == 8) {
|
|
||||||
builder.setPcmEncoding(C.ENCODING_PCM_8BIT);
|
|
||||||
} else if (bps == 16){
|
|
||||||
builder.setPcmEncoding(C.ENCODING_PCM_16BIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (MimeTypes.AUDIO_AAC.equals(mimeType) && audioFormat.getCbSize() > 0) {
|
|
||||||
builder.setInitializationData(Collections.singletonList(audioFormat.getCodecData()));
|
|
||||||
}
|
|
||||||
trackOutput.format(builder.build());
|
|
||||||
idTrackMap.put('0' | (('0' + streamId) << 8) | ('w' << 16) | ('b' << 24),
|
|
||||||
new AviTrack(streamId, streamHeader, trackOutput));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
streamId++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (streamFormat == null) {
|
||||||
|
Log.w(TAG, "Missing Stream Format");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Format.Builder builder = new Format.Builder();
|
||||||
|
builder.setId(streamId);
|
||||||
|
final StreamNameBox streamName = streamList.getChild(StreamNameBox.class);
|
||||||
|
if (streamName != null) {
|
||||||
|
builder.setLabel(streamName.getName());
|
||||||
|
}
|
||||||
|
if (streamHeader.isVideo()) {
|
||||||
|
final String mimeType = streamHeader.getMimeType();
|
||||||
|
if (mimeType == null) {
|
||||||
|
Log.w(TAG, "Unknown FourCC: " + toString(streamHeader.getFourCC()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final VideoFormat videoFormat = streamFormat.getVideoFormat();
|
||||||
|
final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_VIDEO);
|
||||||
|
builder.setWidth(videoFormat.getWidth());
|
||||||
|
builder.setHeight(videoFormat.getHeight());
|
||||||
|
builder.setFrameRate(streamHeader.getFrameRate());
|
||||||
|
builder.setSampleMimeType(mimeType);
|
||||||
|
|
||||||
|
final AviTrack aviTrack;
|
||||||
|
switch (mimeType) {
|
||||||
|
case MimeTypes.VIDEO_MP4V:
|
||||||
|
aviTrack = new Mp4vAviTrack(streamId, streamHeader, trackOutput, builder);
|
||||||
|
break;
|
||||||
|
case MimeTypes.VIDEO_H264:
|
||||||
|
aviTrack = new AvcAviTrack(streamId, streamHeader, trackOutput, builder);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
aviTrack = new AviTrack(streamId, streamHeader, trackOutput);
|
||||||
|
}
|
||||||
|
trackOutput.format(builder.build());
|
||||||
|
idTrackMap.put('0' | (('0' + streamId) << 8) | ('d' << 16) | ('c' << 24), aviTrack);
|
||||||
|
durationUs = streamHeader.getUsPerSample() * streamHeader.getLength();
|
||||||
|
} else if (streamHeader.isAudio()) {
|
||||||
|
final AudioFormat audioFormat = streamFormat.getAudioFormat();
|
||||||
|
final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_AUDIO);
|
||||||
|
final String mimeType = audioFormat.getMimeType();
|
||||||
|
builder.setSampleMimeType(mimeType);
|
||||||
|
//builder.setCodecs(audioFormat.getCodec());
|
||||||
|
builder.setChannelCount(audioFormat.getChannels());
|
||||||
|
builder.setSampleRate(audioFormat.getSamplesPerSecond());
|
||||||
|
if (audioFormat.getFormatTag() == AudioFormat.WAVE_FORMAT_PCM) {
|
||||||
|
final short bps = audioFormat.getBitsPerSample();
|
||||||
|
if (bps == 8) {
|
||||||
|
builder.setPcmEncoding(C.ENCODING_PCM_8BIT);
|
||||||
|
} else if (bps == 16){
|
||||||
|
builder.setPcmEncoding(C.ENCODING_PCM_16BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (MimeTypes.AUDIO_AAC.equals(mimeType) && audioFormat.getCbSize() > 0) {
|
||||||
|
builder.setInitializationData(Collections.singletonList(audioFormat.getCodecData()));
|
||||||
|
}
|
||||||
|
trackOutput.format(builder.build());
|
||||||
|
idTrackMap.put('0' | (('0' + streamId) << 8) | ('w' << 16) | ('b' << 24),
|
||||||
|
new AviTrack(streamId, streamHeader, trackOutput));
|
||||||
|
}
|
||||||
|
streamId++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output.endTracks();
|
output.endTracks();
|
||||||
@ -290,7 +300,7 @@ public class AviExtractor implements Extractor {
|
|||||||
final long size = getUInt(byteBuffer);
|
final long size = getUInt(byteBuffer);
|
||||||
final long position = input.getPosition();
|
final long position = input.getPosition();
|
||||||
//-4 because we over read for the LIST type
|
//-4 because we over read for the LIST type
|
||||||
long nextBox = position + size - 4;
|
long nextBox = alignPosition(position + size - 4);
|
||||||
if (tag == ListBox.LIST) {
|
if (tag == ListBox.LIST) {
|
||||||
final int listType = byteBuffer.getInt();
|
final int listType = byteBuffer.getInt();
|
||||||
if (listType == MOVI) {
|
if (listType == MOVI) {
|
||||||
@ -430,7 +440,7 @@ public class AviExtractor implements Extractor {
|
|||||||
if (id == ListBox.LIST) {
|
if (id == ListBox.LIST) {
|
||||||
seekPosition.position = input.getPosition() + 4;
|
seekPosition.position = input.getPosition() + 4;
|
||||||
} else {
|
} else {
|
||||||
seekPosition.position = input.getPosition() + size;
|
seekPosition.position = alignPosition(input.getPosition() + size);
|
||||||
if (id != JUNK) {
|
if (id != JUNK) {
|
||||||
Log.w(TAG, "Unknown tag=" + toString(id) + " pos=" + (input.getPosition() - 8)
|
Log.w(TAG, "Unknown tag=" + toString(id) + " pos=" + (input.getPosition() - 8)
|
||||||
+ " size=" + size + " moviEnd=" + moviEnd);
|
+ " size=" + size + " moviEnd=" + moviEnd);
|
||||||
|
@ -6,7 +6,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class BoxFactory {
|
public class BoxFactory {
|
||||||
static int[] types = {AviHeaderBox.AVIH, StreamHeaderBox.STRH, StreamFormatBox.STRF};
|
static int[] types = {AviHeaderBox.AVIH, StreamHeaderBox.STRH, StreamFormatBox.STRF, StreamNameBox.STRN};
|
||||||
static {
|
static {
|
||||||
Arrays.sort(types);
|
Arrays.sort(types);
|
||||||
}
|
}
|
||||||
@ -23,6 +23,8 @@ public class BoxFactory {
|
|||||||
return new StreamHeaderBox(type, size, boxBuffer);
|
return new StreamHeaderBox(type, size, boxBuffer);
|
||||||
case StreamFormatBox.STRF:
|
case StreamFormatBox.STRF:
|
||||||
return new StreamFormatBox(type, size, boxBuffer);
|
return new StreamFormatBox(type, size, boxBuffer);
|
||||||
|
case StreamNameBox.STRN:
|
||||||
|
return new StreamNameBox(type, size, boxBuffer);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.google.android.exoplayer2.extractor.avi;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class StreamNameBox extends ResidentBox {
|
||||||
|
public static final int STRN = 's' | ('t' << 8) | ('r' << 16) | ('n' << 24);
|
||||||
|
|
||||||
|
StreamNameBox(int type, int size, ByteBuffer byteBuffer) {
|
||||||
|
super(type, size, byteBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
int len = byteBuffer.capacity();
|
||||||
|
if (byteBuffer.get(len - 1) == 0) {
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
final byte[] bytes = new byte[len];
|
||||||
|
byteBuffer.position(0);
|
||||||
|
byteBuffer.get(bytes);
|
||||||
|
return new String(bytes);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class DataHelper {
|
public class DataHelper {
|
||||||
//Base path "\ExoPlayer\library\extractor\."
|
//Base path "\ExoPlayer\library\extractor\."
|
||||||
@ -43,4 +44,10 @@ public class DataHelper {
|
|||||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
return new StreamFormatBox(StreamFormatBox.STRF, buffer.length, byteBuffer);
|
return new StreamFormatBox(StreamFormatBox.STRF, buffer.length, byteBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static StreamNameBox getStreamNameBox(final String name) {
|
||||||
|
byte[] bytes = name.getBytes();
|
||||||
|
bytes = Arrays.copyOf(bytes, bytes.length + 1);
|
||||||
|
return new StreamNameBox(StreamNameBox.STRN, bytes.length, ByteBuffer.wrap(bytes));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.google.android.exoplayer2.extractor.avi;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class ListBuilder {
|
||||||
|
private ByteBuffer byteBuffer;
|
||||||
|
|
||||||
|
public ListBuilder(int listType) {
|
||||||
|
byteBuffer = AviExtractor.allocate(12);
|
||||||
|
byteBuffer.putInt(ListBox.LIST);
|
||||||
|
byteBuffer.putInt(12);
|
||||||
|
byteBuffer.putInt(listType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBox(final ResidentBox box) {
|
||||||
|
long boxLen = 4 + 4 + box.getSize();
|
||||||
|
if ((boxLen & 1) == 1) {
|
||||||
|
boxLen++;
|
||||||
|
}
|
||||||
|
final ByteBuffer boxBuffer = AviExtractor.allocate(byteBuffer.capacity() + (int)boxLen);
|
||||||
|
byteBuffer.clear();
|
||||||
|
boxBuffer.put(byteBuffer);
|
||||||
|
boxBuffer.putInt(box.getType());
|
||||||
|
boxBuffer.putInt((int)box.getSize());
|
||||||
|
boxBuffer.put(box.getByteBuffer());
|
||||||
|
byteBuffer = boxBuffer;
|
||||||
|
}
|
||||||
|
public ByteBuffer build() {
|
||||||
|
byteBuffer.putInt(4, byteBuffer.capacity() - 8);
|
||||||
|
return byteBuffer;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.google.android.exoplayer2.extractor.avi;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class StreamNameBoxTest {
|
||||||
|
@Test
|
||||||
|
public void createStreamName_givenList() throws IOException {
|
||||||
|
final String name = "Test";
|
||||||
|
final ListBuilder listBuilder = new ListBuilder(AviExtractor.STRL);
|
||||||
|
listBuilder.addBox(DataHelper.getStreamNameBox(name));
|
||||||
|
final ByteBuffer listBuffer = listBuilder.build();
|
||||||
|
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().setData(listBuffer.array()).build();
|
||||||
|
fakeExtractorInput.skipFully(8);
|
||||||
|
ListBox listBox = ListBox.newInstance(listBuffer.capacity() - 8, new BoxFactory(), fakeExtractorInput);
|
||||||
|
Assert.assertEquals(1, listBox.getChildren().size());
|
||||||
|
final StreamNameBox streamNameBox = (StreamNameBox) listBox.getChildren().get(0);
|
||||||
|
//Test + nullT = 5 bytes, so verify that the input is properly aligned
|
||||||
|
Assert.assertEquals(0, fakeExtractorInput.getPosition() & 1);
|
||||||
|
Assert.assertEquals(name, streamNameBox.getName());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user