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