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