parent
399e0e4d76
commit
21206bdd26
@ -0,0 +1,81 @@ |
|||||||
|
package com.otaliastudios.cameraview; |
||||||
|
|
||||||
|
/** |
||||||
|
* A preview frame to be processed by {@link FrameProcessor}s. |
||||||
|
*/ |
||||||
|
public class Frame { |
||||||
|
|
||||||
|
private byte[] mData = null; |
||||||
|
private long mTime = -1; |
||||||
|
private int mRotation = 0; |
||||||
|
|
||||||
|
Frame() {} |
||||||
|
|
||||||
|
void set(byte[] data, long time, int rotation) { |
||||||
|
this.mData = data; |
||||||
|
this.mTime = time; |
||||||
|
this.mRotation = rotation; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object obj) { |
||||||
|
// We want a super fast implementation here, do not compare arrays.
|
||||||
|
return obj instanceof Frame && ((Frame) obj).mTime == mTime; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clones the frame, returning a frozen content that will not be overwritten. |
||||||
|
* This can be kept or safely passed to other threads. |
||||||
|
* Using freeze without clearing with {@link #clear()} can result in memory leaks. |
||||||
|
* |
||||||
|
* @return a frozen Frame |
||||||
|
*/ |
||||||
|
public Frame freeze() { |
||||||
|
byte[] data = new byte[mData.length]; |
||||||
|
System.arraycopy(mData, 0, data, 0, mData.length); |
||||||
|
long time = mTime; |
||||||
|
int rotation = mRotation; |
||||||
|
Frame other = new Frame(); |
||||||
|
other.set(data, time, rotation); |
||||||
|
return other; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Disposes the contents of this frame. Can be useful for frozen frames |
||||||
|
* that are not useful anymore. |
||||||
|
*/ |
||||||
|
public void clear() { |
||||||
|
mData = null; |
||||||
|
mRotation = 0; |
||||||
|
mTime = -1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the frame data. |
||||||
|
* @return the frame data |
||||||
|
*/ |
||||||
|
public byte[] getData() { |
||||||
|
return mData; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the milliseconds epoch for this frame, |
||||||
|
* in the {@link System#currentTimeMillis()} reference. |
||||||
|
* |
||||||
|
* @return time data |
||||||
|
*/ |
||||||
|
public long getTime() { |
||||||
|
return mTime; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the clock-wise rotation that should be applied on the data |
||||||
|
* array, such that the resulting frame matches what the user is seeing |
||||||
|
* on screen. |
||||||
|
* |
||||||
|
* @return clock-wise rotation |
||||||
|
*/ |
||||||
|
public int getRotation() { |
||||||
|
return mRotation; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.otaliastudios.cameraview; |
||||||
|
|
||||||
|
import android.support.annotation.NonNull; |
||||||
|
import android.support.annotation.WorkerThread; |
||||||
|
|
||||||
|
/** |
||||||
|
* A FrameProcessor will process {@link Frame}s coming from the camera preview. |
||||||
|
* It must be passed to {@link Cameraview#setFrameProcessor(FrameProcessor)}. |
||||||
|
*/ |
||||||
|
public interface FrameProcessor { |
||||||
|
|
||||||
|
/** |
||||||
|
* Processes the given frame. The frame will hold the correct values only for the |
||||||
|
* duration of this method. When it returns, the frame contents will be replaced. |
||||||
|
* |
||||||
|
* To keep working with the Frame in an async manner, please use {@link Frame#freeze()}, |
||||||
|
* which will return an immutable Frame. In that case you can pass / hold the frame for |
||||||
|
* as long as you want, and then release its contents using {@link Frame#clear()}. |
||||||
|
* |
||||||
|
* @param frame the new frame |
||||||
|
*/ |
||||||
|
@WorkerThread |
||||||
|
void process(@NonNull Frame frame); |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
package com.otaliastudios.cameraview; |
||||||
|
|
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals; |
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
import static org.junit.Assert.assertFalse; |
||||||
|
import static org.junit.Assert.assertNotEquals; |
||||||
|
import static org.junit.Assert.assertNull; |
||||||
|
import static org.junit.Assert.assertTrue; |
||||||
|
|
||||||
|
public class FrameTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDefaults() { |
||||||
|
Frame frame = new Frame(); |
||||||
|
assertEquals(frame.getTime(), -1); |
||||||
|
assertEquals(frame.getRotation(), 0); |
||||||
|
assertNull(frame.getData()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testEquals() { |
||||||
|
Frame f1 = new Frame(); |
||||||
|
f1.set(null, 1000, 90); |
||||||
|
Frame f2 = new Frame(); |
||||||
|
f2.set(new byte[2], 1000, 0); |
||||||
|
assertEquals(f1, f2); |
||||||
|
|
||||||
|
f2.set(new byte[2], 1001, 0); |
||||||
|
assertNotEquals(f1, f2); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testClear() { |
||||||
|
Frame frame = new Frame(); |
||||||
|
frame.set(new byte[2], 1000, 90); |
||||||
|
frame.clear(); |
||||||
|
|
||||||
|
assertEquals(frame.getTime(), -1); |
||||||
|
assertEquals(frame.getRotation(), 0); |
||||||
|
assertNull(frame.getData()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testFreeze() { |
||||||
|
Frame frame = new Frame(); |
||||||
|
byte[] data = new byte[]{0, 1, 5, 0, 7, 3, 4, 5}; |
||||||
|
long time = 1000; |
||||||
|
int rotation = 90; |
||||||
|
frame.set(data, time, rotation); |
||||||
|
|
||||||
|
Frame frozen = frame.freeze(); |
||||||
|
assertArrayEquals(data, frozen.getData()); |
||||||
|
assertEquals(time, frozen.getTime()); |
||||||
|
assertEquals(rotation, frozen.getRotation()); |
||||||
|
|
||||||
|
// Mutate the first, ensure that frozen is not affected
|
||||||
|
frame.set(new byte[]{3, 2, 1}, 50, 180); |
||||||
|
assertArrayEquals(data, frozen.getData()); |
||||||
|
assertEquals(time, frozen.getTime()); |
||||||
|
assertEquals(rotation, frozen.getRotation()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue