|  |  |  | @ -1,6 +1,8 @@ | 
			
		
	
		
			
				
					|  |  |  |  | package com.otaliastudios.cameraview.frame; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import android.graphics.ImageFormat; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.cameraview.CameraLogger; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.cameraview.size.Size; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -21,7 +23,7 @@ import java.util.concurrent.LinkedBlockingQueue; | 
			
		
	
		
			
				
					|  |  |  |  |  * Main methods are: | 
			
		
	
		
			
				
					|  |  |  |  |  * - {@link #setUp(int, Size)}: to set up with size and allocate buffers | 
			
		
	
		
			
				
					|  |  |  |  |  * - {@link #release()}: to release. After release, a manager can be setUp again. | 
			
		
	
		
			
				
					|  |  |  |  |  * - {@link #getFrame(byte[], long, int, Size, int)}: gets a new {@link Frame}. | 
			
		
	
		
			
				
					|  |  |  |  |  * - {@link #getFrame(byte[], long, int)}: gets a new {@link Frame}. | 
			
		
	
		
			
				
					|  |  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  |  * For both byte buffers and frames to get back to the FrameManager pool, all you have to do | 
			
		
	
		
			
				
					|  |  |  |  |  * is call {@link Frame#release()} when done. | 
			
		
	
	
		
			
				
					|  |  |  | @ -31,12 +33,12 @@ import java.util.concurrent.LinkedBlockingQueue; | 
			
		
	
		
			
				
					|  |  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  |  * 1. {@link #BUFFER_MODE_DISPATCH}: in this mode, as soon as we have a buffer, it is dispatched to | 
			
		
	
		
			
				
					|  |  |  |  |  *    the {@link BufferCallback}. The callback should then fill the buffer, and finally call | 
			
		
	
		
			
				
					|  |  |  |  |  *    {@link #getFrame(byte[], long, int, Size, int)} to receive a frame. | 
			
		
	
		
			
				
					|  |  |  |  |  *    {@link #getFrame(byte[], long, int)} to receive a frame. | 
			
		
	
		
			
				
					|  |  |  |  |  *    This is used for Camera1. | 
			
		
	
		
			
				
					|  |  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  |  * 2. {@link #BUFFER_MODE_ENQUEUE}: in this mode, the manager internally keeps a queue of byte buffers, | 
			
		
	
		
			
				
					|  |  |  |  |  *    instead of handing them to the callback. The users can ask for buffers through {@link #getBuffer()}. | 
			
		
	
		
			
				
					|  |  |  |  |  *    This buffer can be filled with data and used to get a frame {@link #getFrame(byte[], long, int, Size, int)}, | 
			
		
	
		
			
				
					|  |  |  |  |  *    This buffer can be filled with data and used to get a frame {@link #getFrame(byte[], long, int)}, | 
			
		
	
		
			
				
					|  |  |  |  |  *    or, in case it was not filled, returned to the queue using {@link #onBufferUnused(byte[])}. | 
			
		
	
		
			
				
					|  |  |  |  |  *    This is used for Camera2. | 
			
		
	
		
			
				
					|  |  |  |  |  */ | 
			
		
	
	
		
			
				
					|  |  |  | @ -55,6 +57,8 @@ public class FrameManager { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private final int mPoolSize; | 
			
		
	
		
			
				
					|  |  |  |  |     private int mBufferSize = -1; | 
			
		
	
		
			
				
					|  |  |  |  |     private Size mFrameSize = null; | 
			
		
	
		
			
				
					|  |  |  |  |     private int mFrameFormat = -1; | 
			
		
	
		
			
				
					|  |  |  |  |     private LinkedBlockingQueue<Frame> mFrameQueue; | 
			
		
	
		
			
				
					|  |  |  |  |     private LinkedBlockingQueue<byte[]> mBufferQueue; | 
			
		
	
		
			
				
					|  |  |  |  |     private BufferCallback mBufferCallback; | 
			
		
	
	
		
			
				
					|  |  |  | @ -94,17 +98,22 @@ public class FrameManager { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Allocates a {@link #mPoolSize} number of buffers. Should be called once | 
			
		
	
		
			
				
					|  |  |  |  |      * the preview size and the bitsPerPixel value are known. | 
			
		
	
		
			
				
					|  |  |  |  |      * the preview size and the image format value are known. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * This method can be called again after {@link #release()} has been called. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param bitsPerPixel bits per pixel, depends on image format | 
			
		
	
		
			
				
					|  |  |  |  |      * @param previewSize the preview size | 
			
		
	
		
			
				
					|  |  |  |  |      * @param format the image format | 
			
		
	
		
			
				
					|  |  |  |  |      * @param size the frame size | 
			
		
	
		
			
				
					|  |  |  |  |      * @return the buffer size | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public int setUp(int bitsPerPixel, @NonNull Size previewSize) { | 
			
		
	
		
			
				
					|  |  |  |  |         // TODO throw if called twice without release?
 | 
			
		
	
		
			
				
					|  |  |  |  |         long sizeInBits = previewSize.getHeight() * previewSize.getWidth() * bitsPerPixel; | 
			
		
	
		
			
				
					|  |  |  |  |     public int setUp(int format, @NonNull Size size) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (isSetUp()) { | 
			
		
	
		
			
				
					|  |  |  |  |             // TODO throw or just reconfigure?
 | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         mFrameSize = size; | 
			
		
	
		
			
				
					|  |  |  |  |         mFrameFormat = format; | 
			
		
	
		
			
				
					|  |  |  |  |         int bitsPerPixel = ImageFormat.getBitsPerPixel(format); | 
			
		
	
		
			
				
					|  |  |  |  |         long sizeInBits = size.getHeight() * size.getWidth() * bitsPerPixel; | 
			
		
	
		
			
				
					|  |  |  |  |         mBufferSize = (int) Math.ceil(sizeInBits / 8.0d); | 
			
		
	
		
			
				
					|  |  |  |  |         for (int i = 0; i < mPoolSize; i++) { | 
			
		
	
		
			
				
					|  |  |  |  |             if (mBufferMode == BUFFER_MODE_DISPATCH) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -116,13 +125,24 @@ public class FrameManager { | 
			
		
	
		
			
				
					|  |  |  |  |         return mBufferSize; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Returns true after {@link #setUp(int, Size)} | 
			
		
	
		
			
				
					|  |  |  |  |      * but before {@link #release()}. | 
			
		
	
		
			
				
					|  |  |  |  |      * Returns false otherwise. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @return true if set up | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     private boolean isSetUp() { | 
			
		
	
		
			
				
					|  |  |  |  |         return mFrameSize != null; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Returns a new byte buffer than can be filled. | 
			
		
	
		
			
				
					|  |  |  |  |      * This can only be called in {@link #BUFFER_MODE_ENQUEUE} mode! Where the frame | 
			
		
	
		
			
				
					|  |  |  |  |      * manager also holds a queue of the byte buffers. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * If not null, the buffer returned by this method can be filled and used to get | 
			
		
	
		
			
				
					|  |  |  |  |      * a new frame through {@link #getFrame(byte[], long, int, Size, int)}. | 
			
		
	
		
			
				
					|  |  |  |  |      * a new frame through {@link #getFrame(byte[], long, int)}. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @return a buffer, or null | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
	
		
			
				
					|  |  |  | @ -143,7 +163,12 @@ public class FrameManager { | 
			
		
	
		
			
				
					|  |  |  |  |         if (mBufferMode != BUFFER_MODE_ENQUEUE) { | 
			
		
	
		
			
				
					|  |  |  |  |             throw new IllegalStateException("Can't call onBufferUnused() when not in BUFFER_MODE_ENQUEUE."); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         mBufferQueue.offer(buffer); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (isSetUp()) { | 
			
		
	
		
			
				
					|  |  |  |  |             mBufferQueue.offer(buffer); | 
			
		
	
		
			
				
					|  |  |  |  |         } else { | 
			
		
	
		
			
				
					|  |  |  |  |             LOG.w("onBufferUnused: buffer was returned but we're not set up anymore."); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
	
		
			
				
					|  |  |  | @ -158,53 +183,38 @@ public class FrameManager { | 
			
		
	
		
			
				
					|  |  |  |  |      * @param data data | 
			
		
	
		
			
				
					|  |  |  |  |      * @param time timestamp | 
			
		
	
		
			
				
					|  |  |  |  |      * @param rotation rotation | 
			
		
	
		
			
				
					|  |  |  |  |      * @param previewSize preview size | 
			
		
	
		
			
				
					|  |  |  |  |      * @param previewFormat format | 
			
		
	
		
			
				
					|  |  |  |  |      * @return a new frame | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     @NonNull | 
			
		
	
		
			
				
					|  |  |  |  |     public Frame getFrame(@NonNull byte[] data, long time, int rotation, @NonNull Size previewSize, int previewFormat) { | 
			
		
	
		
			
				
					|  |  |  |  |     public Frame getFrame(@NonNull byte[] data, long time, int rotation) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (!isSetUp()) { | 
			
		
	
		
			
				
					|  |  |  |  |             throw new IllegalStateException("Can't call getFrame() after releasing or before setUp."); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         Frame frame = mFrameQueue.poll(); | 
			
		
	
		
			
				
					|  |  |  |  |         if (frame != null) { | 
			
		
	
		
			
				
					|  |  |  |  |             LOG.v("getFrame for time:", time, "RECYCLING.", "Data:", data != null); | 
			
		
	
		
			
				
					|  |  |  |  |             LOG.v("getFrame for time:", time, "RECYCLING."); | 
			
		
	
		
			
				
					|  |  |  |  |         } else { | 
			
		
	
		
			
				
					|  |  |  |  |             LOG.v("getFrame for time:", time, "CREATING.", "Data:", data != null); | 
			
		
	
		
			
				
					|  |  |  |  |             LOG.v("getFrame for time:", time, "CREATING."); | 
			
		
	
		
			
				
					|  |  |  |  |             frame = new Frame(this); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         frame.set(data, time, rotation, previewSize, previewFormat); | 
			
		
	
		
			
				
					|  |  |  |  |         frame.setContent(data, time, rotation, mFrameSize, mFrameFormat); | 
			
		
	
		
			
				
					|  |  |  |  |         return frame; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Releases all frames controlled by this manager and | 
			
		
	
		
			
				
					|  |  |  |  |      * clears the pool. | 
			
		
	
		
			
				
					|  |  |  |  |      * In BUFFER_MODE_ENQUEUE, releases also all the buffers. | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public void release() { | 
			
		
	
		
			
				
					|  |  |  |  |         LOG.w("Releasing all frames!"); | 
			
		
	
		
			
				
					|  |  |  |  |         for (Frame frame : mFrameQueue) { | 
			
		
	
		
			
				
					|  |  |  |  |             frame.releaseManager(); | 
			
		
	
		
			
				
					|  |  |  |  |             frame.release(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         mFrameQueue.clear(); | 
			
		
	
		
			
				
					|  |  |  |  |         if (mBufferMode == BUFFER_MODE_ENQUEUE) { | 
			
		
	
		
			
				
					|  |  |  |  |             mBufferQueue.clear(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         mBufferSize = -1; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Called by child frames when they are released. | 
			
		
	
		
			
				
					|  |  |  |  |      * This might be called from old Frames that belong to an old 'setUp' | 
			
		
	
		
			
				
					|  |  |  |  |      * of this FrameManager instance. So the buffer size might be different, | 
			
		
	
		
			
				
					|  |  |  |  |      * for instance. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param frame the released frame | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     void onFrameReleased(@NonNull Frame frame) { | 
			
		
	
		
			
				
					|  |  |  |  |         byte[] buffer = frame.getData(); | 
			
		
	
		
			
				
					|  |  |  |  |         boolean willRecycle = mFrameQueue.offer(frame); | 
			
		
	
		
			
				
					|  |  |  |  |         if (!willRecycle) { | 
			
		
	
		
			
				
					|  |  |  |  |             // If frame queue is full, let's drop everything.
 | 
			
		
	
		
			
				
					|  |  |  |  |             frame.releaseManager(); | 
			
		
	
		
			
				
					|  |  |  |  |         } else { | 
			
		
	
		
			
				
					|  |  |  |  |             // If frame will be recycled, let's recycle the buffer as well.
 | 
			
		
	
		
			
				
					|  |  |  |  |     void onFrameReleased(@NonNull Frame frame, @NonNull byte[] buffer) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (!isSetUp()) return; | 
			
		
	
		
			
				
					|  |  |  |  |         // If frame queue is full, let's drop everything.
 | 
			
		
	
		
			
				
					|  |  |  |  |         // If frame queue accepts this frame, let's recycle the buffer as well.
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (mFrameQueue.offer(frame)) { | 
			
		
	
		
			
				
					|  |  |  |  |             int currSize = buffer.length; | 
			
		
	
		
			
				
					|  |  |  |  |             int reqSize = mBufferSize; | 
			
		
	
		
			
				
					|  |  |  |  |             if (currSize == reqSize) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -216,4 +226,25 @@ public class FrameManager { | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Releases all frames controlled by this manager and | 
			
		
	
		
			
				
					|  |  |  |  |      * clears the pool. | 
			
		
	
		
			
				
					|  |  |  |  |      * In BUFFER_MODE_ENQUEUE, releases also all the buffers. | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public void release() { | 
			
		
	
		
			
				
					|  |  |  |  |         if (!isSetUp()) { | 
			
		
	
		
			
				
					|  |  |  |  |             LOG.w("release called twice. Ignoring."); | 
			
		
	
		
			
				
					|  |  |  |  |             return; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         LOG.i("release: Clearing the frame and buffer queue."); | 
			
		
	
		
			
				
					|  |  |  |  |         mFrameQueue.clear(); | 
			
		
	
		
			
				
					|  |  |  |  |         if (mBufferMode == BUFFER_MODE_ENQUEUE) { | 
			
		
	
		
			
				
					|  |  |  |  |             mBufferQueue.clear(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         mBufferSize = -1; | 
			
		
	
		
			
				
					|  |  |  |  |         mFrameSize = null; | 
			
		
	
		
			
				
					|  |  |  |  |         mFrameFormat = -1; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |