mirror of
https://github.com/samsonjs/media.git
synced 2026-04-02 10:45:51 +00:00
Simplified UnboundedIntArray
(cherry picked from commit b4cb20cd31e44fe641aac94fee7e69456e48356c)
This commit is contained in:
parent
e14617fc32
commit
0b629e4be8
6 changed files with 64 additions and 198 deletions
|
|
@ -31,6 +31,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
|
|||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
|
@ -449,8 +450,8 @@ public class AviExtractor implements Extractor {
|
|||
final int size = indexByteBuffer.getInt();
|
||||
if ((flags & AVIIF_KEYFRAME) == AVIIF_KEYFRAME) {
|
||||
if (chunkHandler.isVideo()) {
|
||||
int indexSize = seekIndexes[videoId].getSize();
|
||||
if (indexSize == 0 || chunkHandler.chunks - seekIndexes[videoId].get(indexSize - 1) >= chunksPerKeyFrame) {
|
||||
int indexSize = seekIndexes[videoId].size;
|
||||
if (indexSize == 0 || chunkHandler.chunks - seekIndexes[videoId].array[indexSize - 1] >= chunksPerKeyFrame) {
|
||||
keyFrameOffsetsDiv2.add(offset / 2);
|
||||
for (@Nullable ChunkHandler seekTrack : chunkHandlers) {
|
||||
if (seekTrack != null) {
|
||||
|
|
@ -469,13 +470,17 @@ public class AviExtractor implements Extractor {
|
|||
if (videoTrack.chunks == keyFrameCounts[videoTrack.getId()]) {
|
||||
videoTrack.setKeyFrames(ChunkHandler.ALL_KEY_FRAMES);
|
||||
} else {
|
||||
videoTrack.setKeyFrames(seekIndexes[videoId].getArray());
|
||||
videoTrack.setKeyFrames(seekIndexes[videoId].pack());
|
||||
}
|
||||
|
||||
//Work-around a bug where the offset is from the start of the file, not "movi"
|
||||
final long seekOffset = firstEntry.getInt(8) > moviOffset ? 0L : moviOffset;
|
||||
int[][] seekIndexArrays = new int[seekIndexes.length][];
|
||||
for (int i=0;i<seekIndexes.length;i++) {
|
||||
seekIndexArrays[i] = seekIndexes[i].pack();
|
||||
}
|
||||
final AviSeekMap seekMap = new AviSeekMap(videoId, videoTrack.clock.durationUs, videoTrack.chunks,
|
||||
keyFrameOffsetsDiv2.getArray(), seekIndexes, seekOffset);
|
||||
keyFrameOffsetsDiv2.pack(), seekIndexArrays, seekOffset);
|
||||
|
||||
i("Video chunks=" + videoTrack.chunks + " us=" + seekMap.getDurationUs());
|
||||
|
||||
|
|
@ -610,6 +615,38 @@ public class AviExtractor implements Extractor {
|
|||
//Intentionally blank
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized unbounded array of ints.
|
||||
* Used primarily to create Index (SeekMap) data.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static class UnboundedIntArray {
|
||||
@NonNull
|
||||
@VisibleForTesting
|
||||
int[] array = new int[8];
|
||||
@VisibleForTesting
|
||||
int size = 0;
|
||||
|
||||
public void add(int v) {
|
||||
if (size == array.length) {
|
||||
grow();
|
||||
}
|
||||
array[size++] = v;
|
||||
}
|
||||
|
||||
public int[] pack() {
|
||||
if (size != array.length) {
|
||||
array = Arrays.copyOf(array, size);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private void grow() {
|
||||
int increase = Math.max(array.length /4, 1);
|
||||
array = Arrays.copyOf(array, increase + array.length + size);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setChunkHandlers(ChunkHandler[] chunkHandlers) {
|
||||
this.chunkHandlers = chunkHandlers;
|
||||
|
|
@ -642,18 +679,10 @@ public class AviExtractor implements Extractor {
|
|||
}
|
||||
|
||||
private static void w(String message) {
|
||||
try {
|
||||
Log.w(TAG, message);
|
||||
} catch (RuntimeException e) {
|
||||
//Catch not mocked for tests
|
||||
}
|
||||
Log.w(TAG, message);
|
||||
}
|
||||
|
||||
private static void i(String message) {
|
||||
try {
|
||||
Log.i(TAG, message);
|
||||
} catch (RuntimeException e) {
|
||||
//Catch not mocked for tests
|
||||
}
|
||||
Log.i(TAG, message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,15 +39,12 @@ public class AviSeekMap implements SeekMap {
|
|||
final long seekOffset;
|
||||
|
||||
public AviSeekMap(int videoId, long usDuration, int videoChunks, int[] keyFrameOffsetsDiv2,
|
||||
UnboundedIntArray[] seekIndexes, long seekOffset) {
|
||||
int[][] seekIndexes, long seekOffset) {
|
||||
this.videoId = videoId;
|
||||
this.videoUsPerChunk = usDuration / videoChunks;
|
||||
this.duration = usDuration;
|
||||
this.keyFrameOffsetsDiv2 = keyFrameOffsetsDiv2;
|
||||
this.seekIndexes = new int[seekIndexes.length][];
|
||||
for (int i=0;i<seekIndexes.length;i++) {
|
||||
this.seekIndexes[i] = seekIndexes[i].getArray();
|
||||
}
|
||||
this.seekIndexes = seekIndexes;
|
||||
this.seekOffset = seekOffset;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Optimized unbounded array of ints.
|
||||
* Used primarily to create Index (SeekMap) data.
|
||||
*/
|
||||
public class UnboundedIntArray {
|
||||
@NonNull
|
||||
@VisibleForTesting
|
||||
int[] array;
|
||||
//uint
|
||||
private int size = 0;
|
||||
|
||||
public UnboundedIntArray() {
|
||||
this(8);
|
||||
}
|
||||
|
||||
public UnboundedIntArray(int size) {
|
||||
if (size < 0) {
|
||||
throw new IllegalArgumentException("Initial size must be positive: " + size);
|
||||
}
|
||||
array = new int[size];
|
||||
}
|
||||
|
||||
public void add(int v) {
|
||||
if (size == array.length) {
|
||||
grow();
|
||||
}
|
||||
array[size++] = v;
|
||||
}
|
||||
|
||||
public int get(final int index) {
|
||||
if (index >= size) {
|
||||
throw new ArrayIndexOutOfBoundsException(index + ">=" + size);
|
||||
}
|
||||
return array[index];
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void pack() {
|
||||
if (size != array.length) {
|
||||
array = Arrays.copyOf(array, size);
|
||||
}
|
||||
}
|
||||
|
||||
protected void grow() {
|
||||
int increase = Math.max(array.length /4, 1);
|
||||
array = Arrays.copyOf(array, increase + array.length + size);
|
||||
}
|
||||
|
||||
public int[] getArray() {
|
||||
pack();
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only works if values are in sequential order
|
||||
*/
|
||||
public int indexOf(int v) {
|
||||
return Arrays.binarySearch(array, v);
|
||||
}
|
||||
}
|
||||
|
|
@ -594,4 +594,18 @@ public class AviExtractorTest {
|
|||
final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) chunkHandler.trackOutput;
|
||||
Assert.assertEquals(size, fakeTrackOutput.getSampleData(0).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unboundIntArray_add_givenExceedsCapacity() {
|
||||
final AviExtractor.UnboundedIntArray unboundedIntArray = new AviExtractor.UnboundedIntArray();
|
||||
final int testLen = unboundedIntArray.array.length + 1;
|
||||
for (int i=0; i < testLen; i++) {
|
||||
unboundedIntArray.add(i);
|
||||
}
|
||||
|
||||
Assert.assertEquals(testLen, unboundedIntArray.size);
|
||||
for (int i=0; i < testLen; i++) {
|
||||
Assert.assertEquals(i, unboundedIntArray.array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,14 +118,12 @@ public class DataHelper {
|
|||
|
||||
public static AviSeekMap getAviSeekMap() {
|
||||
final int[] keyFrameOffsetsDiv2= {4, 1024};
|
||||
final UnboundedIntArray videoArray = new UnboundedIntArray();
|
||||
videoArray.add(0);
|
||||
videoArray.add(4);
|
||||
final UnboundedIntArray audioArray = new UnboundedIntArray();
|
||||
audioArray.add(0);
|
||||
audioArray.add(128);
|
||||
final int[] videoArray = new int[2];
|
||||
videoArray[1] = 4;
|
||||
final int[] audioArray = new int[2];
|
||||
audioArray[1] = 128;
|
||||
return new AviSeekMap(0, 100L, 8, keyFrameOffsetsDiv2,
|
||||
new UnboundedIntArray[]{videoArray, audioArray}, MOVI_OFFSET);
|
||||
new int[][]{videoArray, audioArray}, MOVI_OFFSET);
|
||||
}
|
||||
|
||||
private static void putIndex(final ByteBuffer byteBuffer, int chunkId, int flags, int offset,
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UnboundedIntArrayTest {
|
||||
@Test
|
||||
public void add_givenInt() {
|
||||
final UnboundedIntArray unboundedIntArray = new UnboundedIntArray();
|
||||
unboundedIntArray.add(4);
|
||||
Assert.assertEquals(1, unboundedIntArray.getSize());
|
||||
Assert.assertEquals(unboundedIntArray.getArray()[0], 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexOf_givenOrderSet() {
|
||||
final UnboundedIntArray unboundedIntArray = new UnboundedIntArray();
|
||||
unboundedIntArray.add(2);
|
||||
unboundedIntArray.add(4);
|
||||
unboundedIntArray.add(5);
|
||||
unboundedIntArray.add(8);
|
||||
Assert.assertEquals(2, unboundedIntArray.indexOf(5));
|
||||
Assert.assertTrue(unboundedIntArray.indexOf(6) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void grow_givenSizeOfOne() {
|
||||
final UnboundedIntArray unboundedIntArray = new UnboundedIntArray(1);
|
||||
unboundedIntArray.add(0);
|
||||
Assert.assertEquals(1, unboundedIntArray.getSize());
|
||||
unboundedIntArray.add(1);
|
||||
Assert.assertTrue(unboundedIntArray.getSize() > 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pack_givenSizeOfOne() {
|
||||
final UnboundedIntArray unboundedIntArray = new UnboundedIntArray(8);
|
||||
unboundedIntArray.add(1);
|
||||
unboundedIntArray.add(2);
|
||||
Assert.assertEquals(8, unboundedIntArray.array.length);
|
||||
unboundedIntArray.pack();
|
||||
Assert.assertEquals(2, unboundedIntArray.array.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illegalArgument_givenNegativeSize() {
|
||||
try {
|
||||
new UnboundedIntArray(-1);
|
||||
Assert.fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
//Intentionally blank
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_givenValidIndex() {
|
||||
final UnboundedIntArray unboundedIntArray = new UnboundedIntArray(4);
|
||||
unboundedIntArray.add(1);
|
||||
unboundedIntArray.add(2);
|
||||
Assert.assertEquals(1, unboundedIntArray.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_givenOutOfBounds() {
|
||||
final UnboundedIntArray unboundedIntArray = new UnboundedIntArray(4);
|
||||
try {
|
||||
unboundedIntArray.get(0);
|
||||
Assert.fail();
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
//Intentionally blank
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue