/* * 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() + ")"; } }