Commit 43b8a9b3 by Dustin

Add support for StreamName fixed issues with position alignment

parent 167c2f3f
...@@ -92,6 +92,13 @@ public class AviExtractor implements Extractor { ...@@ -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 { ...@@ -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) { if (streamFormat == null) {
i++; Log.w(TAG, "Missing Stream Format");
if (streamHeader.isVideo()) { continue;
final String mimeType = streamHeader.getMimeType(); }
if (mimeType == null) { final Format.Builder builder = new Format.Builder();
Log.w(TAG, "Unknown FourCC: " + toString(streamHeader.getFourCC())); builder.setId(streamId);
continue; final StreamNameBox streamName = streamList.getChild(StreamNameBox.class);
} if (streamName != null) {
final VideoFormat videoFormat = streamFormat.getVideoFormat(); builder.setLabel(streamName.getName());
final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_VIDEO); }
final Format.Builder builder = new Format.Builder(); if (streamHeader.isVideo()) {
builder.setWidth(videoFormat.getWidth()); final String mimeType = streamHeader.getMimeType();
builder.setHeight(videoFormat.getHeight()); if (mimeType == null) {
builder.setFrameRate(streamHeader.getFrameRate()); Log.w(TAG, "Unknown FourCC: " + toString(streamHeader.getFourCC()));
builder.setSampleMimeType(mimeType); continue;
}
final AviTrack aviTrack; final VideoFormat videoFormat = streamFormat.getVideoFormat();
switch (mimeType) { final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_VIDEO);
case MimeTypes.VIDEO_MP4V: builder.setWidth(videoFormat.getWidth());
aviTrack = new Mp4vAviTrack(streamId, streamHeader, trackOutput, builder); builder.setHeight(videoFormat.getHeight());
break; builder.setFrameRate(streamHeader.getFrameRate());
case MimeTypes.VIDEO_H264: builder.setSampleMimeType(mimeType);
aviTrack = new AvcAviTrack(streamId, streamHeader, trackOutput, builder);
break; final AviTrack aviTrack;
default: switch (mimeType) {
aviTrack = new AviTrack(streamId, streamHeader, trackOutput); case MimeTypes.VIDEO_MP4V:
} aviTrack = new Mp4vAviTrack(streamId, streamHeader, trackOutput, builder);
trackOutput.format(builder.build()); break;
idTrackMap.put('0' | (('0' + streamId) << 8) | ('d' << 16) | ('c' << 24), aviTrack); case MimeTypes.VIDEO_H264:
durationUs = streamHeader.getUsPerSample() * streamHeader.getLength(); aviTrack = new AvcAviTrack(streamId, streamHeader, trackOutput, builder);
} else if (streamHeader.isAudio()) { break;
final AudioFormat audioFormat = streamFormat.getAudioFormat(); default:
final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_AUDIO); aviTrack = new AviTrack(streamId, streamHeader, trackOutput);
final Format.Builder builder = new Format.Builder(); }
final String mimeType = audioFormat.getMimeType(); trackOutput.format(builder.build());
builder.setSampleMimeType(mimeType); idTrackMap.put('0' | (('0' + streamId) << 8) | ('d' << 16) | ('c' << 24), aviTrack);
//builder.setCodecs(audioFormat.getCodec()); durationUs = streamHeader.getUsPerSample() * streamHeader.getLength();
builder.setChannelCount(audioFormat.getChannels()); } else if (streamHeader.isAudio()) {
builder.setSampleRate(audioFormat.getSamplesPerSecond()); final AudioFormat audioFormat = streamFormat.getAudioFormat();
if (audioFormat.getFormatTag() == AudioFormat.WAVE_FORMAT_PCM) { final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_AUDIO);
final short bps = audioFormat.getBitsPerSample(); final String mimeType = audioFormat.getMimeType();
if (bps == 8) { builder.setSampleMimeType(mimeType);
builder.setPcmEncoding(C.ENCODING_PCM_8BIT); //builder.setCodecs(audioFormat.getCodec());
} else if (bps == 16){ builder.setChannelCount(audioFormat.getChannels());
builder.setPcmEncoding(C.ENCODING_PCM_16BIT); builder.setSampleRate(audioFormat.getSamplesPerSecond());
} if (audioFormat.getFormatTag() == AudioFormat.WAVE_FORMAT_PCM) {
} final short bps = audioFormat.getBitsPerSample();
if (MimeTypes.AUDIO_AAC.equals(mimeType) && audioFormat.getCbSize() > 0) { if (bps == 8) {
builder.setInitializationData(Collections.singletonList(audioFormat.getCodecData())); builder.setPcmEncoding(C.ENCODING_PCM_8BIT);
} } else if (bps == 16){
trackOutput.format(builder.build()); builder.setPcmEncoding(C.ENCODING_PCM_16BIT);
idTrackMap.put('0' | (('0' + streamId) << 8) | ('w' << 16) | ('b' << 24),
new AviTrack(streamId, streamHeader, trackOutput));
}
} }
streamId++;
} }
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 { ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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;
} }
......
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; ...@@ -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 { ...@@ -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));
}
} }
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;
}
}
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());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment