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
17package androidx.media.filterfw;
18
19import 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 */
53public 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     */
71    public final FrameType getType() {
72        return mBackingStore.getFrameType();
73    }
74
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     */
84    public final void setTimestamp(long timestamp) {
85        mBackingStore.setTimestamp(timestamp);
86    }
87
88    /**
89     * @return the frame's timestamp in nanoseconds.
90     */
91    public final long getTimestamp() {
92        return mBackingStore.getTimestamp();
93    }
94
95    /**
96     * @return the frame's timestamp in milliseconds.
97     */
98    public final long getTimestampMillis() {
99        return mBackingStore.getTimestamp() / 1000000L;
100    }
101
102    public final boolean isReadOnly() {
103        return mReadOnly;
104    }
105
106    public final FrameValue asFrameValue() {
107        return FrameValue.create(mBackingStore);
108    }
109
110    public final FrameValues asFrameValues() {
111        return FrameValues.create(mBackingStore);
112    }
113
114    public final FrameBuffer1D asFrameBuffer1D() {
115        return FrameBuffer1D.create(mBackingStore);
116    }
117
118    public final FrameBuffer2D asFrameBuffer2D() {
119        return FrameBuffer2D.create(mBackingStore);
120    }
121
122    public final FrameImage2D asFrameImage2D() {
123        return FrameImage2D.create(mBackingStore);
124    }
125
126    @Override
127    public String toString() {
128        return "Frame[" + getType().toString() + "]: " + mBackingStore;
129    }
130
131    @Override
132    public boolean equals(Object object) {
133        return object instanceof Frame && ((Frame)object).mBackingStore == mBackingStore;
134    }
135
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
145    public final Frame release() {
146        mBackingStore = mBackingStore.release();
147        return mBackingStore != null ? this : null;
148    }
149
150    public final Frame retain() {
151        mBackingStore = mBackingStore.retain();
152        return this;
153    }
154
155    public void unlock() {
156        if (!mBackingStore.unlock()) {
157            throw new RuntimeException("Attempting to unlock frame that is not locked!");
158        }
159    }
160
161    public int[] getDimensions() {
162        int[] dim = mBackingStore.getDimensions();
163        return dim != null ? Arrays.copyOf(dim, dim.length) : null;
164    }
165
166    Frame(FrameType type, int[] dimensions, FrameManager manager) {
167        mBackingStore = new BackingStore(type, dimensions, manager);
168    }
169
170    Frame(BackingStore backingStore) {
171        mBackingStore = backingStore;
172    }
173
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
181    final void setReadOnly(boolean readOnly) {
182        mReadOnly = readOnly;
183    }
184
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
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