| /* |
| * 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; |
| } |
| } |
| |