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      * If there are multiple pending buffers, {@link Allocation#ioReceive receive} the latest one.
    103      *
    104      * <p>Does not block if there are no currently pending buffers.</p>
    105      *
    106      * @return {@code true} only if any buffers were received.
    107      *
    108      * @throws IllegalStateException If this object has already been {@link #close closed}
    109      */
    110     public synchronized boolean receiveLatestAvailableBuffers() {
    111         checkNotClosed();
    112 
    113         int updatedBuffers = 0;
    114         while (mListener.isBufferPending()) {
    115             mListener.waitForBuffer();
    116             mAllocation.ioReceive();
    117             updatedBuffers++;
    118         }
    119 
    120         if (VERBOSE) Log.v(TAG, "receiveLatestAvailableBuffers - updated = " + updatedBuffers);
    121 
    122         return updatedBuffers > 0;
    123     }
    124 
    125     /**
    126      * Closes the object and detaches the listener from the {@link Allocation}.
    127      *
    128      * <p>This has a side effect of calling {@link #receiveLatestAvailableBuffers}
    129      *
    130      * <p>Does <i>not</i> destroy the underlying {@link Allocation}.</p>
    131      */
    132     @Override
    133     public synchronized void close() {
    134         if (mClosed) return;
    135 
    136         receiveLatestAvailableBuffers();
    137         mAllocation.setOnBufferAvailableListener(/*callback*/null);
    138         mClosed = true;
    139     }
    140 
    141     protected void checkNotClosed() {
    142         if (mClosed) {
    143             throw new IllegalStateException(TAG + " has been closed");
    144         }
    145     }
    146 
    147     @Override
    148     protected void finalize() throws Throwable {
    149         try {
    150             close();
    151         } finally {
    152             super.finalize();
    153         }
    154     }
    155 
    156     private BlockingInputAllocation(Allocation allocation) {
    157         mAllocation = allocation;
    158 
    159         mListener = new OnBufferAvailableListener();
    160         mAllocation.setOnBufferAvailableListener(mListener);
    161     }
    162 
    163     // TODO: refactor with the ImageReader Listener code to use a LinkedBlockingQueue
    164     private static class OnBufferAvailableListener implements Allocation.OnBufferAvailableListener {
    165         private int mPendingBuffers = 0;
    166         private final Object mBufferSyncObject = new Object();
    167         private static final int TIMEOUT_MS = 5000;
    168 
    169         public boolean isBufferPending() {
    170             synchronized (mBufferSyncObject) {
    171                 return (mPendingBuffers > 0);
    172             }
    173         }
    174 
    175         /**
    176          * Waits for a buffer. Caller must call ioReceive exactly once after calling this.
    177          *
    178          * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
    179          */
    180         public void waitForBuffer() {
    181             synchronized (mBufferSyncObject) {
    182                 while (mPendingBuffers == 0) {
    183                     try {
    184                         if (VERBOSE) Log.v(TAG, "waiting for next buffer");
    185                         mBufferSyncObject.wait(TIMEOUT_MS);
    186                         if (mPendingBuffers == 0) {
    187                             throw new TimeoutRuntimeException("wait for buffer image timed out");
    188                         }
    189                     } catch (InterruptedException ie) {
    190                         throw new AssertionError(ie);
    191                     }
    192                 }
    193                 mPendingBuffers--;
    194             }
    195         }
    196 
    197         @Override
    198         public void onBufferAvailable(Allocation a) {
    199             if (VERBOSE) Log.v(TAG, "new buffer in allocation available");
    200             synchronized (mBufferSyncObject) {
    201                 mPendingBuffers++;
    202                 mBufferSyncObject.notifyAll();
    203             }
    204         }
    205     }
    206 }
    207