| /* |
| * Copyright (C) 2011 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 android.filterfw.core; |
| |
| import android.filterfw.core.Frame; |
| import android.filterfw.core.FrameFormat; |
| import android.filterfw.core.FrameManager; |
| import android.filterfw.format.ObjectFormat; |
| import android.graphics.Bitmap; |
| |
| import java.io.InputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.OutputStream; |
| import java.nio.ByteBuffer; |
| |
| /** |
| * A frame that serializes any assigned values. Such a frame is used when passing data objects |
| * between threads. |
| * |
| * @hide |
| */ |
| public class SerializedFrame extends Frame { |
| |
| /** |
| * The initial capacity of the serialized data stream. |
| */ |
| private final static int INITIAL_CAPACITY = 64; |
| |
| /** |
| * The internal data streams. |
| */ |
| private DirectByteOutputStream mByteOutputStream; |
| private ObjectOutputStream mObjectOut; |
| |
| /** |
| * An unsynchronized output stream that writes data to an accessible byte array. Callers are |
| * responsible for synchronization. This is more efficient than a ByteArrayOutputStream, as |
| * there are no array copies or synchronization involved to read back written data. |
| */ |
| private class DirectByteOutputStream extends OutputStream { |
| private byte[] mBuffer = null; |
| private int mOffset = 0; |
| private int mDataOffset = 0; |
| |
| public DirectByteOutputStream(int size) { |
| mBuffer = new byte[size]; |
| } |
| |
| private final void ensureFit(int bytesToWrite) { |
| if (mOffset + bytesToWrite > mBuffer.length) { |
| byte[] oldBuffer = mBuffer; |
| mBuffer = new byte[Math.max(mOffset + bytesToWrite, mBuffer.length * 2)]; |
| System.arraycopy(oldBuffer, 0, mBuffer, 0, mOffset); |
| oldBuffer = null; |
| } |
| } |
| |
| public final void markHeaderEnd() { |
| mDataOffset = mOffset; |
| } |
| |
| public final int getSize() { |
| return mOffset; |
| } |
| |
| public byte[] getByteArray() { |
| return mBuffer; |
| } |
| |
| @Override |
| public final void write(byte b[]) { |
| write(b, 0, b.length); |
| } |
| |
| @Override |
| public final void write(byte b[], int off, int len) { |
| ensureFit(len); |
| System.arraycopy(b, off, mBuffer, mOffset, len); |
| mOffset += len; |
| } |
| |
| @Override |
| public final void write(int b) { |
| ensureFit(1); |
| mBuffer[mOffset++] = (byte)b; |
| } |
| |
| public final void reset() { |
| mOffset = mDataOffset; |
| } |
| |
| public final DirectByteInputStream getInputStream() { |
| return new DirectByteInputStream(mBuffer, mOffset); |
| } |
| } |
| |
| /** |
| * An unsynchronized input stream that reads data directly from a provided byte array. Callers |
| * are responsible for synchronization and ensuring that the byte buffer is valid. |
| */ |
| private class DirectByteInputStream extends InputStream { |
| |
| private byte[] mBuffer; |
| private int mPos = 0; |
| private int mSize; |
| |
| public DirectByteInputStream(byte[] buffer, int size) { |
| mBuffer = buffer; |
| mSize = size; |
| } |
| |
| @Override |
| public final int available() { |
| return mSize - mPos; |
| } |
| |
| @Override |
| public final int read() { |
| return (mPos < mSize) ? (mBuffer[mPos++] & 0xFF) : -1; |
| } |
| |
| @Override |
| public final int read(byte[] b, int off, int len) { |
| if (mPos >= mSize) { |
| return -1; |
| } |
| if ((mPos + len) > mSize) { |
| len = mSize - mPos; |
| } |
| System.arraycopy(mBuffer, mPos, b, off, len); |
| mPos += len; |
| return len; |
| } |
| |
| @Override |
| public final long skip(long n) { |
| if ((mPos + n) > mSize) { |
| n = mSize - mPos; |
| } |
| if (n < 0) { |
| return 0; |
| } |
| mPos += n; |
| return n; |
| } |
| } |
| |
| SerializedFrame(FrameFormat format, FrameManager frameManager) { |
| super(format, frameManager); |
| setReusable(false); |
| |
| // Setup streams |
| try { |
| mByteOutputStream = new DirectByteOutputStream(INITIAL_CAPACITY); |
| mObjectOut = new ObjectOutputStream(mByteOutputStream); |
| mByteOutputStream.markHeaderEnd(); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not create serialization streams for " |
| + "SerializedFrame!", e); |
| } |
| } |
| |
| static SerializedFrame wrapObject(Object object, FrameManager frameManager) { |
| FrameFormat format = ObjectFormat.fromObject(object, FrameFormat.TARGET_SIMPLE); |
| SerializedFrame result = new SerializedFrame(format, frameManager); |
| result.setObjectValue(object); |
| return result; |
| } |
| |
| @Override |
| protected boolean hasNativeAllocation() { |
| return false; |
| } |
| |
| @Override |
| protected void releaseNativeAllocation() { |
| } |
| |
| @Override |
| public Object getObjectValue() { |
| return deserializeObjectValue(); |
| } |
| |
| @Override |
| public void setInts(int[] ints) { |
| assertFrameMutable(); |
| setGenericObjectValue(ints); |
| } |
| |
| @Override |
| public int[] getInts() { |
| Object result = deserializeObjectValue(); |
| return (result instanceof int[]) ? (int[])result : null; |
| } |
| |
| @Override |
| public void setFloats(float[] floats) { |
| assertFrameMutable(); |
| setGenericObjectValue(floats); |
| } |
| |
| @Override |
| public float[] getFloats() { |
| Object result = deserializeObjectValue(); |
| return (result instanceof float[]) ? (float[])result : null; |
| } |
| |
| @Override |
| public void setData(ByteBuffer buffer, int offset, int length) { |
| assertFrameMutable(); |
| setGenericObjectValue(ByteBuffer.wrap(buffer.array(), offset, length)); |
| } |
| |
| @Override |
| public ByteBuffer getData() { |
| Object result = deserializeObjectValue(); |
| return (result instanceof ByteBuffer) ? (ByteBuffer)result : null; |
| } |
| |
| @Override |
| public void setBitmap(Bitmap bitmap) { |
| assertFrameMutable(); |
| setGenericObjectValue(bitmap); |
| } |
| |
| @Override |
| public Bitmap getBitmap() { |
| Object result = deserializeObjectValue(); |
| return (result instanceof Bitmap) ? (Bitmap)result : null; |
| } |
| |
| @Override |
| protected void setGenericObjectValue(Object object) { |
| serializeObjectValue(object); |
| } |
| |
| private final void serializeObjectValue(Object object) { |
| try { |
| mByteOutputStream.reset(); |
| mObjectOut.writeObject(object); |
| mObjectOut.flush(); |
| mObjectOut.close(); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not serialize object " + object + " in " |
| + this + "!", e); |
| } |
| } |
| |
| private final Object deserializeObjectValue() { |
| try { |
| InputStream inputStream = mByteOutputStream.getInputStream(); |
| ObjectInputStream objectStream = new ObjectInputStream(inputStream); |
| return objectStream.readObject(); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not deserialize object in " + this + "!", e); |
| } catch (ClassNotFoundException e) { |
| throw new RuntimeException("Unable to deserialize object of unknown class in " |
| + this + "!", e); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "SerializedFrame (" + getFormat() + ")"; |
| } |
| } |