mirror of
https://github.com/androidx/media.git
synced 2025-05-08 08:00:49 +08:00
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() {
|
||||
this(0);
|
||||
}
|
||||
@ -212,70 +219,73 @@ public class AviExtractor implements Extractor {
|
||||
for (Box box : headerList.getChildren()) {
|
||||
if (box instanceof ListBox && ((ListBox) box).getListType() == STRL) {
|
||||
final ListBox streamList = (ListBox) box;
|
||||
final List<Box> streamChildren = streamList.getChildren();
|
||||
for (int i=0;i<streamChildren.size();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);
|
||||
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++;
|
||||
}
|
||||
final StreamHeaderBox streamHeader = streamList.getChild(StreamHeaderBox.class);
|
||||
final StreamFormatBox streamFormat = streamList.getChild(StreamFormatBox.class);
|
||||
if (streamHeader == null) {
|
||||
Log.w(TAG, "Missing Stream Header");
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
@ -290,7 +300,7 @@ public class AviExtractor implements Extractor {
|
||||
final long size = getUInt(byteBuffer);
|
||||
final long position = input.getPosition();
|
||||
//-4 because we over read for the LIST type
|
||||
long nextBox = position + size - 4;
|
||||
long nextBox = alignPosition(position + size - 4);
|
||||
if (tag == ListBox.LIST) {
|
||||
final int listType = byteBuffer.getInt();
|
||||
if (listType == MOVI) {
|
||||
@ -430,7 +440,7 @@ public class AviExtractor implements Extractor {
|
||||
if (id == ListBox.LIST) {
|
||||
seekPosition.position = input.getPosition() + 4;
|
||||
} else {
|
||||
seekPosition.position = input.getPosition() + size;
|
||||
seekPosition.position = alignPosition(input.getPosition() + size);
|
||||
if (id != JUNK) {
|
||||
Log.w(TAG, "Unknown tag=" + toString(id) + " pos=" + (input.getPosition() - 8)
|
||||
+ " size=" + size + " moviEnd=" + moviEnd);
|
||||
|
@ -6,7 +6,7 @@ import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BoxFactory {
|
||||
static int[] types = {AviHeaderBox.AVIH, StreamHeaderBox.STRH, StreamFormatBox.STRF};
|
||||
static int[] types = {AviHeaderBox.AVIH, StreamHeaderBox.STRH, StreamFormatBox.STRF, StreamNameBox.STRN};
|
||||
static {
|
||||
Arrays.sort(types);
|
||||
}
|
||||
@ -23,6 +23,8 @@ public class BoxFactory {
|
||||
return new StreamHeaderBox(type, size, boxBuffer);
|
||||
case StreamFormatBox.STRF:
|
||||
return new StreamFormatBox(type, size, boxBuffer);
|
||||
case StreamNameBox.STRN:
|
||||
return new StreamNameBox(type, size, boxBuffer);
|
||||
default:
|
||||
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.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DataHelper {
|
||||
//Base path "\ExoPlayer\library\extractor\."
|
||||
@ -43,4 +44,10 @@ public class DataHelper {
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
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