/* * 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 androidx.media.filterfw; import java.util.Arrays; /** * Frames are the data containers that are transported between Filters. * * Frames may be used only within a Filter during filter graph execution. Accessing Frames outside * of graph execution may cause unexpected results. * * There are two ways to obtain new Frame instances. You can call * {@link OutputPort#fetchAvailableFrame(int[])} on an OutputPort to obtain a Frame to pass to an * output. You can also call {@link #create(FrameType, int[])} to obtain * a detached Frame instance that you may hold onto in your filter. If you need to hold on to a * Frame that is owned by an input or output queue, you must call * {@link #retain()} on it. * * When you are done using a detached Frame, you must release it yourself. * * To access frame data, call any of the {@code lock}-methods. This will give you access to the * frame data in the desired format. You must pass in a {@code mode} indicating whether you wish * to read or write to the data. Writing to a read-locked Frame may produce unexpected results and * interfere with other filters. When you are done reading or writing to the data, you must call * {@link #unlock()}. Note, that a Frame must be unlocked before you push it into an output queue. * * Generally, any type of access format to a Frame's data will be granted. However, it is strongly * recommended to specify the access format that you intend to use in your filter's signature or * in the access flags passed to {@code newFrame()}. This will allow the Frame to allocate * the most efficient backings for the intended type of access. * * A frame can be be pushed to an OutputPort by calling the {@link OutputPort#pushFrame(Frame)} * method. Frames that have been pushed become read-only, and can no longer be modified. * * On the other end, a Filter can pull in an input Frame by calling {@link InputPort#pullFrame()} * on the desired InputPort. Such frames are always read-only. */ public class Frame { /** Special timestamp value indicating that no time-stamp was set. */ public static final long TIMESTAMP_NOT_SET = -1; /** Frame data access mode: Read */ public static final int MODE_READ = 1; /** Frame data access mode: Write */ public static final int MODE_WRITE = 2; BackingStore mBackingStore; boolean mReadOnly = false; // Public API ////////////////////////////////////////////////////////////////////////////////// /** * Returns the frame's type. * @return A FrameType instance describing the frame data-type. */ public final FrameType getType() { return mBackingStore.getFrameType(); } public final int getElementCount() { return mBackingStore.getElementCount(); } /** * Set the frame's timestamp in nanoseconds. * * @param timestamp the timestamp of this frame in nanoseconds. */ public final void setTimestamp(long timestamp) { mBackingStore.setTimestamp(timestamp); } /** * @return the frame's timestamp in nanoseconds. */ public final long getTimestamp() { return mBackingStore.getTimestamp(); } /** * @return the frame's timestamp in milliseconds. */ public final long getTimestampMillis() { return mBackingStore.getTimestamp() / 1000000L; } public final boolean isReadOnly() { return mReadOnly; } public final FrameValue asFrameValue() { return FrameValue.create(mBackingStore); } public final FrameValues asFrameValues() { return FrameValues.create(mBackingStore); } public final FrameBuffer1D asFrameBuffer1D() { return FrameBuffer1D.create(mBackingStore); } public final FrameBuffer2D asFrameBuffer2D() { return FrameBuffer2D.create(mBackingStore); } public final FrameImage2D asFrameImage2D() { return FrameImage2D.create(mBackingStore); } @Override public String toString() { return "Frame[" + getType().toString() + "]: " + mBackingStore; } @Override public boolean equals(Object object) { return object instanceof Frame && ((Frame)object).mBackingStore == mBackingStore; } public static Frame create(FrameType type, int[] dimensions) { FrameManager manager = FrameManager.current(); if (manager == null) { throw new IllegalStateException("Attempting to create new Frame outside of " + "FrameManager context!"); } return new Frame(type, dimensions, manager); } public final Frame release() { mBackingStore = mBackingStore.release(); return mBackingStore != null ? this : null; } public final Frame retain() { mBackingStore = mBackingStore.retain(); return this; } public void unlock() { if (!mBackingStore.unlock()) { throw new RuntimeException("Attempting to unlock frame that is not locked!"); } } public int[] getDimensions() { int[] dim = mBackingStore.getDimensions(); return dim != null ? Arrays.copyOf(dim, dim.length) : null; } Frame(FrameType type, int[] dimensions, FrameManager manager) { mBackingStore = new BackingStore(type, dimensions, manager); } Frame(BackingStore backingStore) { mBackingStore = backingStore; } final void assertAccessible(int mode) { // Make sure frame is in write-mode if (mReadOnly && mode == MODE_WRITE) { throw new RuntimeException("Attempting to write to read-only frame " + this + "!"); } } final void setReadOnly(boolean readOnly) { mReadOnly = readOnly; } void resize(int[] newDims) { int[] oldDims = mBackingStore.getDimensions(); int oldCount = oldDims == null ? 0 : oldDims.length; int newCount = newDims == null ? 0 : newDims.length; if (oldCount != newCount) { throw new IllegalArgumentException("Cannot resize " + oldCount + "-dimensional " + "Frame to " + newCount + "-dimensional Frame!"); } else if (newDims != null && !Arrays.equals(oldDims, newDims)) { mBackingStore.resize(newDims); } } Frame makeCpuCopy(FrameManager frameManager) { Frame frame = new Frame(getType(), getDimensions(), frameManager); frame.mBackingStore.importStore(mBackingStore); return frame; } }