Home | History | Annotate | Download | only in rs
      1 /*
      2  * Copyright 2014 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 android.hardware.camera2.cts.rs;
     18 
     19 import static android.hardware.camera2.cts.helpers.Preconditions.*;
     20 
     21 import android.hardware.camera2.cts.helpers.UncheckedCloseable;
     22 import android.renderscript.Allocation;
     23 import android.util.Log;
     24 
     25 import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
     26 
     27 /**
     28  * An {@link Allocation} wrapper that can be used to block until new buffers are available.
     29  *
     30  * <p>Can only be used only with {@link Allocation#USAGE_IO_INPUT} usage Allocations.</p>
     31  *
     32  * <p>When used with a {@link android.hardware.camera2.CameraDevice CameraDevice} this
     33  * must be used as an output surface.</p>
     34  */
     35 class BlockingInputAllocation implements UncheckedCloseable {
     36 
     37     private static final String TAG = BlockingInputAllocation.class.getSimpleName();
     38     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     39 
     40     private final Allocation mAllocation;
     41     private final OnBufferAvailableListener mListener;
     42     private boolean mClosed;
     43 
     44     /**
     45      * Wrap an existing Allocation with this {@link BlockingInputAllocation}.
     46      *
     47      * <p>Doing this will clear any existing associated buffer listeners and replace
     48      * it with a new one.</p>
     49      *
     50      * @param allocation A non-{@code null} {@link Allocation allocation}
     51      * @return a new {@link BlockingInputAllocation} instance
     52      *
     53      * @throws NullPointerException
     54      *           If {@code allocation} was {@code null}
     55      * @throws IllegalArgumentException
     56      *           If {@code allocation}'s usage did not have one of USAGE_IO_INPUT or USAGE_IO_OUTPUT
     57      * @throws IllegalStateException
     58      *           If this object has already been {@link #close closed}
     59      */
     60     public static BlockingInputAllocation wrap(Allocation allocation) {
     61         checkNotNull("allocation", allocation);
     62         checkBitFlags("usage", allocation.getUsage(), "USAGE_IO_INPUT", Allocation.USAGE_IO_INPUT);
     63 
     64         return new BlockingInputAllocation(allocation);
     65     }
     66 
     67     /**
     68      * Get the Allocation backing this {@link BlockingInputAllocation}.
     69      *
     70      * @return Allocation instance (non-{@code null}).
     71      *
     72      * @throws IllegalStateException If this object has already been {@link #close closed}
     73      */
     74     public Allocation getAllocation() {
     75         checkNotClosed();
     76 
     77         return mAllocation;
     78     }
     79 
     80     /**
     81      * Waits for a buffer to become available, then immediately
     82      * {@link Allocation#ioReceive receives} it.
     83      *
     84      * <p>After calling this, the next script used with this allocation will use the
     85      * newer buffer.</p>
     86      *
     87      * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
     88      * @throws IllegalStateException If this object has already been {@link #close closed}
     89      */
     90     public synchronized void waitForBufferAndReceive() {
     91         checkNotClosed();
     92 
     93         if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - begin");
     94 
     95         mListener.waitForBuffer();
     96         mAllocation.ioReceive();
     97 
     98         if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - Allocation#ioReceive");
     99     }
    100 
    101     /**
    102      * Waits for a buffer to become available, then immediately
    103      * {@link Allocation#ioReceive receives} it.
    104      *
    105      * <p>After calling this, the next script used with this allocation will use the
    106      * newer buffer.</p>
    107      *
    108      * @param timeoutMs timeout in milliseconds.
    109      *
    110      * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
    111      * @throws IllegalStateException If this object has already been {@link #close closed}
    112      */
    113     public synchronized void waitForBufferAndReceive(long timeoutMs) {
    114         checkNotClosed();
    115 
    116         if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - begin");
    117 
    118         mListener.waitForBuffer(timeoutMs);
    119         mAllocation.ioReceive();
    120 
    121         if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - Allocation#ioReceive");
    122     }
    123 
    124     /**
    125      * If there are multiple pending buffers, {@link Allocation#ioReceive receive} the latest one.
    126      *
    127      * <p>Does not block if there are no currently pending buffers.</p>
    128      *
    129      * @return {@code true} only if any buffers were received.
    130      *
    131      * @throws IllegalStateException If this object has already been {@link #close closed}
    132      */
    133     public synchronized boolean receiveLatestAvailableBuffers() {
    134         checkNotClosed();
    135 
    136         int updatedBuffers = 0;
    137         while (mListener.isBufferPending()) {
    138             mListener.waitForBuffer();
    139             mAllocation.ioReceive();
    140             updatedBuffers++;
    141         }
    142 
    143         if (VERBOSE) Log.v(TAG, "receiveLatestAvailableBuffers - updated = " + updatedBuffers);
    144 
    145         return updatedBuffers > 0;
    146     }
    147 
    148     /**
    149      * Closes the object and detaches the listener from the {@link Allocation}.
    150      *
    151      * <p>This has a side effect of calling {@link #receiveLatestAvailableBuffers}
    152      *
    153      * <p>Does <i>not</i> destroy the underlying {@link Allocation}.</p>
    154      */
    155     @Override
    156     public synchronized void close() {
    157         if (mClosed) return;
    158 
    159         receiveLatestAvailableBuffers();
    160         mAllocation.setOnBufferAvailableListener(/*callback*/null);
    161         mClosed = true;
    162     }
    163 
    164     protected void checkNotClosed() {
    165         if (mClosed) {
    166             throw new IllegalStateException(TAG + " has been closed");
    167         }
    168     }
    169 
    170     @Override
    171     protected void finalize() throws Throwable {
    172         try {
    173             close();
    174         } finally {
    175             super.finalize();
    176         }
    177     }
    178 
    179     private BlockingInputAllocation(Allocation allocation) {
    180         mAllocation = allocation;
    181 
    182         mListener = new OnBufferAvailableListener();
    183         mAllocation.setOnBufferAvailableListener(mListener);
    184     }
    185 
    186     // TODO: refactor with the ImageReader Listener code to use a LinkedBlockingQueue
    187     private static class OnBufferAvailableListener implements Allocation.OnBufferAvailableListener {
    188         private int mPendingBuffers = 0;
    189         private final Object mBufferSyncObject = new Object();
    190         private static final int TIMEOUT_MS = 20000;
    191 
    192         public boolean isBufferPending() {
    193             synchronized (mBufferSyncObject) {
    194                 return (mPendingBuffers > 0);
    195             }
    196         }
    197 
    198         /**
    199          * Waits for a buffer. Caller must call ioReceive exactly once after calling this.
    200          *
    201          * @param timeoutMs wait timeout in milliseconds
    202          *
    203          * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
    204          */
    205         private void waitForBufferWithTimeout(long timeoutMs) {
    206             synchronized (mBufferSyncObject) {
    207                 while (mPendingBuffers == 0) {
    208                     try {
    209                         if (VERBOSE) Log.v(TAG, "waiting for next buffer");
    210                         mBufferSyncObject.wait(timeoutMs);
    211                         if (mPendingBuffers == 0) {
    212                             throw new TimeoutRuntimeException("wait for buffer image timed out");
    213                         }
    214                     } catch (InterruptedException ie) {
    215                         throw new AssertionError(ie);
    216                     }
    217                 }
    218                 mPendingBuffers--;
    219             }
    220         }
    221 
    222         /**
    223          * Waits for a buffer. Caller must call ioReceive exactly once after calling this.
    224          *
    225          * @param timeoutMs wait timeout in milliseconds.
    226          *
    227          * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
    228          */
    229         public void waitForBuffer(long timeoutMs) {
    230             if (timeoutMs <= TIMEOUT_MS) {
    231                 waitForBufferWithTimeout(TIMEOUT_MS);
    232             } else {
    233                 waitForBufferWithTimeout(timeoutMs + TIMEOUT_MS);
    234             }
    235         }
    236 
    237         /**
    238          * Waits for a buffer. Caller must call ioReceive exactly once after calling this.
    239          *
    240          * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
    241          */
    242         public void waitForBuffer() {
    243             waitForBufferWithTimeout(TIMEOUT_MS);
    244         }
    245 
    246         @Override
    247         public void onBufferAvailable(Allocation a) {
    248             if (VERBOSE) Log.v(TAG, "new buffer in allocation available");
    249             synchronized (mBufferSyncObject) {
    250                 mPendingBuffers++;
    251                 mBufferSyncObject.notifyAll();
    252             }
    253         }
    254     }
    255 }
    256