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
19/**
20 * Output ports are the data emitting ports of filters.
21 * <p>
22 * Filters push data frames onto output-ports, which in turn push them onto their connected input
23 * ports. Output ports must be connected to an input port before data can be pushed onto them.
24 * Input and output ports share their Frame slot, meaning that when a frame is waiting on an output
25 * port, it is also waiting on the connected input port.
26 * </p><p>
27 * Only one frame can be pushed onto an output port at a time. In other words, a Frame must first
28 * be consumed by the target filter before a new frame can be pushed on the output port. If the
29 * output port is set to wait until it becomes free (see {@link #setWaitsUntilAvailable(boolean)}),
30 * it is guaranteed to be available when {@code onProcess()} is called. This is the default setting.
31 * </p>
32 */
33public final class OutputPort {
34
35    private Filter mFilter;
36    private String mName;
37    private Signature.PortInfo mInfo;
38    private FrameQueue.Builder mQueueBuilder = null;
39    private FrameQueue mQueue = null;
40    private boolean mWaitsUntilAvailable = true;
41    private InputPort mTarget = null;
42
43    /**
44     * Returns true, if this port is connected to a target port.
45     * @return true, if this port is connected to a target port.
46     */
47    public boolean isConnected() {
48        return mTarget != null;
49    }
50
51    /**
52     * Returns true, if there is no frame waiting on this port.
53     * @return true, if no Frame instance is waiting on this port.
54     */
55    public boolean isAvailable() {
56        return mQueue == null || mQueue.canPush();
57    }
58
59    /**
60     * Returns a frame for writing.
61     *
62     * Call this method to fetch a new frame to write into. When you have finished writing the
63     * frame data, you can push it into the output queue using {@link #pushFrame(Frame)}. Note,
64     * that the Frame returned is owned by the queue. If you wish to hold on to the frame, you
65     * must detach it.
66     *
67     * @param dimensions the size of the Frame you wish to obtain.
68     * @return a writable Frame instance.
69     */
70    public Frame fetchAvailableFrame(int[] dimensions) {
71        Frame frame = getQueue().fetchAvailableFrame(dimensions);
72        if (frame != null) {
73            //Log.i("OutputPort", "Adding frame " + frame + " to auto-release pool");
74            mFilter.addAutoReleaseFrame(frame);
75        }
76        return frame;
77    }
78
79    /**
80     * Pushes a frame onto this output port.
81     *
82     * This is typically a Frame instance you obtained by previously calling
83     * {@link #fetchAvailableFrame(int[])}, but may come from other sources such as an input port
84     * that is attached to this output port.
85     *
86     * Once you have pushed a frame to an output, you may no longer modify it as it may be shared
87     * among other filters.
88     *
89     * @param frame the frame to push to the output queue.
90     */
91    public void pushFrame(Frame frame) {
92        // Some queues allow pushing without fetching, so we need to make sure queue is open
93        // before pushing!
94        long timestamp = frame.getTimestamp();
95        if (timestamp == Frame.TIMESTAMP_NOT_SET)
96            frame.setTimestamp(mFilter.getCurrentTimestamp());
97        getQueue().pushFrame(frame);
98    }
99
100    /**
101     * Sets whether to wait until this port becomes available before processing.
102     * When set to true, the Filter will not be scheduled for processing unless there is no Frame
103     * waiting on this port. The default value is true.
104     *
105     * @param wait true, if filter should wait for the port to become available before processing.
106     * @see #waitsUntilAvailable()
107     */
108    public void setWaitsUntilAvailable(boolean wait) {
109        mWaitsUntilAvailable = wait;
110    }
111
112    /**
113     * Returns whether the filter waits until this port is available before processing.
114     * @return true, if the filter waits until this port is available before processing.
115     * @see #setWaitsUntilAvailable(boolean)
116     */
117    public boolean waitsUntilAvailable() {
118        return mWaitsUntilAvailable;
119    }
120
121    /**
122     * Returns the output port's name.
123     * This is the name that was specified when the output port was connected.
124     *
125     * @return the output port's name.
126     */
127    public String getName() {
128        return mName;
129    }
130
131    /**
132     * Return the filter object that this port belongs to.
133     *
134     * @return the output port's filter.
135     */
136    public Filter getFilter() {
137        return mFilter;
138    }
139
140    @Override
141    public String toString() {
142        return mFilter.getName() + ":" + mName;
143    }
144
145    OutputPort(Filter filter, String name, Signature.PortInfo info) {
146        mFilter = filter;
147        mName = name;
148        mInfo = info;
149    }
150
151    void setTarget(InputPort target) {
152        mTarget = target;
153    }
154
155    /**
156     * Return the (input) port that this output port is connected to.
157     *
158     * @return the connected port, null if not connected.
159     */
160    public InputPort getTarget() {
161        return mTarget;
162    }
163
164    FrameQueue getQueue() {
165        return mQueue;
166    }
167
168    void setQueue(FrameQueue queue) {
169        mQueue = queue;
170        mQueueBuilder = null;
171    }
172
173    void onOpen(FrameQueue.Builder builder) {
174        mQueueBuilder = builder;
175        mQueueBuilder.setWriteType(mInfo.type);
176        mFilter.onOutputPortOpen(this);
177    }
178
179    boolean isOpen() {
180        return mQueue != null;
181    }
182
183    final boolean conditionsMet() {
184        return !mWaitsUntilAvailable || isAvailable();
185    }
186
187    void clear() {
188        if (mQueue != null) {
189            mQueue.clear();
190        }
191    }
192}
193
194