mirror of
https://github.com/androidx/media.git
synced 2025-05-12 01:59:50 +08:00
Simplify AviTrack
This commit is contained in:
parent
1528b8b5ee
commit
aee15f6c70
@ -258,7 +258,7 @@ public class AviExtractor implements Extractor {
|
|||||||
builder.setSampleMimeType(mimeType);
|
builder.setSampleMimeType(mimeType);
|
||||||
|
|
||||||
final LinearClock clock = new LinearClock(durationUs, length);
|
final LinearClock clock = new LinearClock(durationUs, length);
|
||||||
aviTrack = new AviTrack(streamId, C.TRACK_TYPE_VIDEO, clock, trackOutput);
|
aviTrack = new AviTrack(streamId, AviTrack.CHUNK_TYPE_VIDEO, clock, trackOutput);
|
||||||
|
|
||||||
if (MimeTypes.VIDEO_H264.equals(mimeType)) {
|
if (MimeTypes.VIDEO_H264.equals(mimeType)) {
|
||||||
final AvcChunkPeeker avcChunkPeeker = new AvcChunkPeeker(builder, trackOutput, clock);
|
final AvcChunkPeeker avcChunkPeeker = new AvcChunkPeeker(builder, trackOutput, clock);
|
||||||
@ -294,7 +294,7 @@ public class AviExtractor implements Extractor {
|
|||||||
builder.setInitializationData(Collections.singletonList(audioFormat.getCodecData()));
|
builder.setInitializationData(Collections.singletonList(audioFormat.getCodecData()));
|
||||||
}
|
}
|
||||||
trackOutput.format(builder.build());
|
trackOutput.format(builder.build());
|
||||||
aviTrack = new AviTrack(streamId, C.TRACK_TYPE_AUDIO,
|
aviTrack = new AviTrack(streamId, AviTrack.CHUNK_TYPE_AUDIO,
|
||||||
new LinearClock(durationUs, length), trackOutput);
|
new LinearClock(durationUs, length), trackOutput);
|
||||||
aviTrack.setKeyFrames(AviTrack.ALL_KEY_FRAMES);
|
aviTrack.setKeyFrames(AviTrack.ALL_KEY_FRAMES);
|
||||||
}else {
|
}else {
|
||||||
@ -370,18 +370,18 @@ public class AviExtractor implements Extractor {
|
|||||||
if (aviTrack != null) {
|
if (aviTrack != null) {
|
||||||
if (aviTrack.isAudio()) {
|
if (aviTrack.isAudio()) {
|
||||||
final long durationUs = aviTrack.getClock().durationUs;
|
final long durationUs = aviTrack.getClock().durationUs;
|
||||||
i("Audio #" + aviTrack.id + " chunks: " + aviTrack.chunks + " us=" + durationUs +
|
i("Audio #" + aviTrack.getId() + " chunks: " + aviTrack.chunks + " us=" + durationUs +
|
||||||
" size=" + aviTrack.size);
|
" size=" + aviTrack.size);
|
||||||
final LinearClock linearClock = aviTrack.getClock();
|
final LinearClock linearClock = aviTrack.getClock();
|
||||||
//If the audio track duration is off from the video by >5 % recalc using video
|
//If the audio track duration is off from the video by >5 % recalc using video
|
||||||
if ((durationUs - videoDuration) / (float)videoDuration > .05f) {
|
if ((durationUs - videoDuration) / (float)videoDuration > .05f) {
|
||||||
w("Audio #" + aviTrack.id + " duration is off using videoDuration");
|
w("Audio #" + aviTrack.getId() + " duration is off using videoDuration");
|
||||||
linearClock.setDuration(videoDuration);
|
linearClock.setDuration(videoDuration);
|
||||||
}
|
}
|
||||||
linearClock.setLength(aviTrack.chunks);
|
linearClock.setLength(aviTrack.chunks);
|
||||||
if (aviTrack.chunks != keyFrameCounts[aviTrack.id]) {
|
if (aviTrack.chunks != keyFrameCounts[aviTrack.getId()]) {
|
||||||
w("Audio is not all key frames chunks=" + aviTrack.chunks + " keyFrames=" +
|
w("Audio is not all key frames chunks=" + aviTrack.chunks + " keyFrames=" +
|
||||||
keyFrameCounts[aviTrack.id]);
|
keyFrameCounts[aviTrack.getId()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,7 +406,7 @@ public class AviExtractor implements Extractor {
|
|||||||
final ByteBuffer firstEntry = AviExtractor.allocate(16);
|
final ByteBuffer firstEntry = AviExtractor.allocate(16);
|
||||||
input.peekFully(firstEntry.array(), 0, 16);
|
input.peekFully(firstEntry.array(), 0, 16);
|
||||||
|
|
||||||
final int videoId = videoTrack.id;
|
final int videoId = videoTrack.getId();
|
||||||
final ByteBuffer indexByteBuffer = allocate(Math.min(remaining, 64 * 1024));
|
final ByteBuffer indexByteBuffer = allocate(Math.min(remaining, 64 * 1024));
|
||||||
final byte[] bytes = indexByteBuffer.array();
|
final byte[] bytes = indexByteBuffer.array();
|
||||||
|
|
||||||
@ -453,19 +453,19 @@ public class AviExtractor implements Extractor {
|
|||||||
keyFrameOffsetsDiv2.add(offset / 2);
|
keyFrameOffsetsDiv2.add(offset / 2);
|
||||||
for (AviTrack seekTrack : aviTracks) {
|
for (AviTrack seekTrack : aviTracks) {
|
||||||
if (seekTrack != null) {
|
if (seekTrack != null) {
|
||||||
seekIndexes[seekTrack.id].add(seekTrack.chunks);
|
seekIndexes[seekTrack.getId()].add(seekTrack.chunks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keyFrameCounts[aviTrack.id]++;
|
keyFrameCounts[aviTrack.getId()]++;
|
||||||
}
|
}
|
||||||
aviTrack.chunks++;
|
aviTrack.chunks++;
|
||||||
aviTrack.size+=size;
|
aviTrack.size+=size;
|
||||||
}
|
}
|
||||||
indexByteBuffer.compact();
|
indexByteBuffer.compact();
|
||||||
}
|
}
|
||||||
if (videoTrack.chunks == keyFrameCounts[videoTrack.id]) {
|
if (videoTrack.chunks == keyFrameCounts[videoTrack.getId()]) {
|
||||||
videoTrack.setKeyFrames(AviTrack.ALL_KEY_FRAMES);
|
videoTrack.setKeyFrames(AviTrack.ALL_KEY_FRAMES);
|
||||||
} else {
|
} else {
|
||||||
videoTrack.setKeyFrames(seekIndexes[videoId].getArray());
|
videoTrack.setKeyFrames(seekIndexes[videoId].getArray());
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.avi;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||||
@ -29,10 +30,8 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public class AviTrack {
|
public class AviTrack {
|
||||||
public static final int[] ALL_KEY_FRAMES = new int[0];
|
public static final int[] ALL_KEY_FRAMES = new int[0];
|
||||||
|
public static int CHUNK_TYPE_VIDEO = ('d' << 16) | ('c' << 24);
|
||||||
final int id;
|
public static int CHUNK_TYPE_AUDIO = ('w' << 16) | ('b' << 24);
|
||||||
|
|
||||||
final @C.TrackType int trackType;
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
LinearClock clock;
|
LinearClock clock;
|
||||||
@ -57,34 +56,22 @@ public class AviTrack {
|
|||||||
transient int chunkSize;
|
transient int chunkSize;
|
||||||
transient int chunkRemaining;
|
transient int chunkRemaining;
|
||||||
|
|
||||||
private static int getChunkIdLower(int id) {
|
@VisibleForTesting
|
||||||
|
static int getChunkIdLower(int id) {
|
||||||
int tens = id / 10;
|
int tens = id / 10;
|
||||||
int ones = id % 10;
|
int ones = id % 10;
|
||||||
return ('0' + tens) | (('0' + ones) << 8);
|
return ('0' + tens) | (('0' + ones) << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getVideoChunkId(int id) {
|
AviTrack(int id, int chunkType, @NonNull LinearClock clock,
|
||||||
return getChunkIdLower(id) | ('d' << 16) | ('c' << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getAudioChunkId(int id) {
|
|
||||||
return getChunkIdLower(id) | ('w' << 16) | ('b' << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
AviTrack(int id, @C.TrackType int trackType, @NonNull LinearClock clock,
|
|
||||||
@NonNull TrackOutput trackOutput) {
|
@NonNull TrackOutput trackOutput) {
|
||||||
this.id = id;
|
this.chunkId = getChunkIdLower(id) | chunkType;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.trackType = trackType;
|
|
||||||
this.trackOutput = trackOutput;
|
this.trackOutput = trackOutput;
|
||||||
if (isVideo()) {
|
if (isVideo()) {
|
||||||
chunkId = getVideoChunkId(id);
|
|
||||||
chunkIdAlt = getChunkIdLower(id) | ('d' << 16) | ('b' << 24);
|
chunkIdAlt = getChunkIdLower(id) | ('d' << 16) | ('b' << 24);
|
||||||
} else if (isAudio()) {
|
|
||||||
chunkId = getAudioChunkId(id);
|
|
||||||
chunkIdAlt = 0xffff;
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unknown Track Type: " + trackType);
|
chunkIdAlt = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,11 +105,11 @@ public class AviTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVideo() {
|
public boolean isVideo() {
|
||||||
return trackType == C.TRACK_TYPE_VIDEO;
|
return (chunkId & CHUNK_TYPE_VIDEO) == CHUNK_TYPE_VIDEO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAudio() {
|
public boolean isAudio() {
|
||||||
return trackType == C.TRACK_TYPE_AUDIO;
|
return (chunkId & CHUNK_TYPE_AUDIO) == CHUNK_TYPE_AUDIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
|
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
|
||||||
@ -169,4 +156,8 @@ public class AviTrack {
|
|||||||
//Log.d(AviExtractor.TAG, "Frame: " + (isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " size=" + size + " frame=" + clock.getIndex() + " key=" + isKeyFrame());
|
//Log.d(AviExtractor.TAG, "Frame: " + (isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " size=" + size + " frame=" + clock.getIndex() + " key=" + isKeyFrame());
|
||||||
clock.advance();
|
clock.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return ((chunkId >> 8) & 0xf) + ( chunkId & 0xf) * 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,7 +473,7 @@ public class AviExtractorTest {
|
|||||||
final AviTrack aviTrack = aviExtractor.getVideoTrack();
|
final AviTrack aviTrack = aviExtractor.getVideoTrack();
|
||||||
final long position = DataHelper.MOVI_OFFSET + aviSeekMap.keyFrameOffsetsDiv2[1] * 2L;
|
final long position = DataHelper.MOVI_OFFSET + aviSeekMap.keyFrameOffsetsDiv2[1] * 2L;
|
||||||
aviExtractor.seek(position, 0L);
|
aviExtractor.seek(position, 0L);
|
||||||
Assert.assertEquals(aviSeekMap.seekIndexes[aviTrack.id][1], aviTrack.getClock().getIndex());
|
Assert.assertEquals(aviSeekMap.seekIndexes[aviTrack.getId()][1], aviTrack.getClock().getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -76,7 +76,7 @@ public class DataHelper {
|
|||||||
final ArrayList<Box> list = new ArrayList<>(2);
|
final ArrayList<Box> list = new ArrayList<>(2);
|
||||||
list.add(streamHeaderBox);
|
list.add(streamHeaderBox);
|
||||||
list.add(streamFormatBox);
|
list.add(streamFormatBox);
|
||||||
return new ListBox((int)(streamHeaderBox.getSize() + streamFormatBox.getSize()),
|
return new ListBox(streamHeaderBox.getSize() + streamFormatBox.getSize(),
|
||||||
ListBox.TYPE_STRL, list);
|
ListBox.TYPE_STRL, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +106,14 @@ public class DataHelper {
|
|||||||
|
|
||||||
public static AviTrack getVideoAviTrack(int sec) {
|
public static AviTrack getVideoAviTrack(int sec) {
|
||||||
final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false);
|
final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false);
|
||||||
return new AviTrack(0, C.TRACK_TYPE_VIDEO,
|
return new AviTrack(0, AviTrack.CHUNK_TYPE_VIDEO,
|
||||||
new LinearClock(sec * 1_000_000L, sec * FPS),
|
new LinearClock(sec * 1_000_000L, sec * FPS),
|
||||||
fakeTrackOutput);
|
fakeTrackOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AviTrack getAudioAviTrack(int sec) {
|
public static AviTrack getAudioAviTrack(int sec) {
|
||||||
final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false);
|
final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false);
|
||||||
return new AviTrack(AUDIO_ID, C.TRACK_TYPE_AUDIO,
|
return new AviTrack(AUDIO_ID, AviTrack.CHUNK_TYPE_AUDIO,
|
||||||
new LinearClock(sec * 1_000_000L, sec * FPS * AUDIO_PER_VIDEO),
|
new LinearClock(sec * 1_000_000L, sec * FPS * AUDIO_PER_VIDEO),
|
||||||
fakeTrackOutput);
|
fakeTrackOutput);
|
||||||
}
|
}
|
||||||
@ -153,8 +153,8 @@ public class DataHelper {
|
|||||||
*/
|
*/
|
||||||
public static ByteBuffer getIndex(final int secs, final int keyFrameRate, int offset) {
|
public static ByteBuffer getIndex(final int secs, final int keyFrameRate, int offset) {
|
||||||
final int videoFrames = secs * FPS;
|
final int videoFrames = secs * FPS;
|
||||||
final int videoChunkId = AviTrack.getVideoChunkId(0);
|
final int videoChunkId = AviTrack.CHUNK_TYPE_VIDEO | AviTrack.getChunkIdLower(0);
|
||||||
final int audioChunkId = AviTrack.getAudioChunkId(1);
|
final int audioChunkId = AviTrack.CHUNK_TYPE_AUDIO | AviTrack.getChunkIdLower(1);
|
||||||
final ByteBuffer byteBuffer = AviExtractor.allocate((videoFrames + videoFrames*AUDIO_PER_VIDEO) * 16);
|
final ByteBuffer byteBuffer = AviExtractor.allocate((videoFrames + videoFrames*AUDIO_PER_VIDEO) * 16);
|
||||||
|
|
||||||
for (int v=0;v<videoFrames;v++) {
|
for (int v=0;v<videoFrames;v++) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user