1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.media.filterfw;
18 
19 import java.util.Arrays;
20 
21 /**
22  * Frames are the data containers that are transported between Filters.
23  *
24  * Frames may be used only within a Filter during filter graph execution. Accessing Frames outside
25  * of graph execution may cause unexpected results.
26  *
27  * There are two ways to obtain new Frame instances. You can call
28  * {@link OutputPort#fetchAvailableFrame(int[])} on an OutputPort to obtain a Frame to pass to an
29  * output. You can also call {@link #create(FrameType, int[])} to obtain
30  * a detached Frame instance that you may hold onto in your filter. If you need to hold on to a
31  * Frame that is owned by an input or output queue, you must call
32  * {@link #retain()} on it.
33  *
34  * When you are done using a detached Frame, you must release it yourself.
35  *
36  * To access frame data, call any of the {@code lock}-methods. This will give you access to the
37  * frame data in the desired format. You must pass in a {@code mode} indicating whether you wish
38  * to read or write to the data. Writing to a read-locked Frame may produce unexpected results and
39  * interfere with other filters. When you are done reading or writing to the data, you must call
40  * {@link #unlock()}. Note, that a Frame must be unlocked before you push it into an output queue.
41  *
42  * Generally, any type of access format to a Frame's data will be granted. However, it is strongly
43  * recommended to specify the access format that you intend to use in your filter's signature or
44  * in the access flags passed to {@code newFrame()}. This will allow the Frame to allocate
45  * the most efficient backings for the intended type of access.
46  *
47  * A frame can be be pushed to an OutputPort by calling the {@link OutputPort#pushFrame(Frame)}
48  * method. Frames that have been pushed become read-only, and can no longer be modified.
49  *
50  * On the other end, a Filter can pull in an input Frame by calling {@link InputPort#pullFrame()}
51  * on the desired InputPort. Such frames are always read-only.
52  */
53 public class Frame {
54 
55     /** Special timestamp value indicating that no time-stamp was set. */
56     public static final long TIMESTAMP_NOT_SET = -1;
57 
58     /** Frame data access mode: Read */
59     public static final int MODE_READ = 1;
60     /** Frame data access mode: Write */
61     public static final int MODE_WRITE = 2;
62 
63     BackingStore mBackingStore;
64     boolean mReadOnly = false;
65 
66     // Public API //////////////////////////////////////////////////////////////////////////////////
67     /**
68      * Returns the frame's type.
69      * @return A FrameType instance describing the frame data-type.
70      */
getType()71     public final FrameType getType() {
72         return mBackingStore.getFrameType();
73     }
74 
getElementCount()75     public final int getElementCount() {
76         return mBackingStore.getElementCount();
77     }
78 
79     /**
80      * Set the frame's timestamp in nanoseconds.
81      *
82      * @param timestamp the timestamp of this frame in nanoseconds.
83      */
setTimestamp(long timestamp)84     public final void setTimestamp(long timestamp) {
85         mBackingStore.setTimestamp(timestamp);
86     }
87 
88     /**
89      * @return the frame's timestamp in nanoseconds.
90      */
getTimestamp()91     public final long getTimestamp() {
92         return mBackingStore.getTimestamp();
93     }
94 
95     /**
96      * @return the frame's timestamp in milliseconds.
97      */
getTimestampMillis()98     public final long getTimestampMillis() {
99         return mBackingStore.getTimestamp() / 1000000L;
100     }
101 
isReadOnly()102     public final boolean isReadOnly() {
103         return mReadOnly;
104     }
105 
asFrameValue()106     public final FrameValue asFrameValue() {
107         return FrameValue.create(mBackingStore);
108     }
109 
asFrameValues()110     public final FrameValues asFrameValues() {
111         return FrameValues.create(mBackingStore);
112     }
113 
asFrameBuffer1D()114     public final FrameBuffer1D asFrameBuffer1D() {
115         return FrameBuffer1D.create(mBackingStore);
116     }
117 
asFrameBuffer2D()118     public final FrameBuffer2D asFrameBuffer2D() {
119         return FrameBuffer2D.create(mBackingStore);
120     }
121 
asFrameImage2D()122     public final FrameImage2D asFrameImage2D() {
123         return FrameImage2D.create(mBackingStore);
124     }
125 
126     @Override
toString()127     public String toString() {
128         return "Frame[" + getType().toString() + "]: " + mBackingStore;
129     }
130 
131     @Override
equals(Object object)132     public boolean equals(Object object) {
133         return object instanceof Frame && ((Frame)object).mBackingStore == mBackingStore;
134     }
135 
create(FrameType type, int[] dimensions)136     public static Frame create(FrameType type, int[] dimensions) {
137         FrameManager manager = FrameManager.current();
138         if (manager == null) {
139             throw new IllegalStateException("Attempting to create new Frame outside of "
140                 + "FrameManager context!");
141         }
142         return new Frame(type, dimensions, manager);
143     }
144 
release()145     public final Frame release() {
146         mBackingStore = mBackingStore.release();
147         return mBackingStore != null ? this : null;
148     }
149 
retain()150     public final Frame retain() {
151         mBackingStore = mBackingStore.retain();
152         return this;
153     }
154 
unlock()155     public void unlock() {
156         if (!mBackingStore.unlock()) {
157             throw new RuntimeException("Attempting to unlock frame that is not locked!");
158         }
159     }
160 
getDimensions()161     public int[] getDimensions() {
162         int[] dim = mBackingStore.getDimensions();
163         return dim != null ? Arrays.copyOf(dim, dim.length) : null;
164     }
165 
Frame(FrameType type, int[] dimensions, FrameManager manager)166     Frame(FrameType type, int[] dimensions, FrameManager manager) {
167         mBackingStore = new BackingStore(type, dimensions, manager);
168     }
169 
Frame(BackingStore backingStore)170     Frame(BackingStore backingStore) {
171         mBackingStore = backingStore;
172     }
173 
assertAccessible(int mode)174     final void assertAccessible(int mode) {
175         // Make sure frame is in write-mode
176         if (mReadOnly && mode == MODE_WRITE) {
177             throw new RuntimeException("Attempting to write to read-only frame " + this + "!");
178         }
179     }
180 
setReadOnly(boolean readOnly)181     final void setReadOnly(boolean readOnly) {
182         mReadOnly = readOnly;
183     }
184 
resize(int[] newDims)185     void resize(int[] newDims) {
186         int[] oldDims = mBackingStore.getDimensions();
187         int oldCount = oldDims == null ? 0 : oldDims.length;
188         int newCount = newDims == null ? 0 : newDims.length;
189         if (oldCount != newCount) {
190             throw new IllegalArgumentException("Cannot resize " + oldCount + "-dimensional "
191                 + "Frame to " + newCount + "-dimensional Frame!");
192         } else if (newDims != null && !Arrays.equals(oldDims, newDims)) {
193             mBackingStore.resize(newDims);
194         }
195     }
196 
makeCpuCopy(FrameManager frameManager)197     Frame makeCpuCopy(FrameManager frameManager) {
198         Frame frame = new Frame(getType(), getDimensions(), frameManager);
199         frame.mBackingStore.importStore(mBackingStore);
200         return frame;
201     }
202 }
203 
204