Home | History | Annotate | Download | only in camera2proxy
      1 /*
      2  * Copyright (C) 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 com.android.camera.one.v2.camera2proxy;
     18 
     19 import android.graphics.Rect;
     20 import android.media.Image;
     21 
     22 import com.google.common.base.Objects;
     23 import com.google.common.collect.ImmutableList;
     24 
     25 import java.nio.ByteBuffer;
     26 import java.util.ArrayList;
     27 import java.util.List;
     28 
     29 import javax.annotation.concurrent.GuardedBy;
     30 import javax.annotation.concurrent.ThreadSafe;
     31 
     32 /**
     33  * An {@link ImageProxy} backed by an {@link android.media.Image}.
     34  */
     35 @ThreadSafe
     36 public class AndroidImageProxy implements ImageProxy {
     37 
     38     /**
     39      * An {@link ImageProxy.Plane} backed by an
     40      * {@link android.media.Image.Plane}.
     41      */
     42     public class Plane implements ImageProxy.Plane {
     43         private final int mPixelStride;
     44         private final int mRowStride;
     45         private final ByteBuffer mBuffer;
     46 
     47         public Plane(Image.Plane imagePlane) {
     48             // Copying out the contents of the Image.Plane means that this Plane
     49             // implementation can be thread-safe (without requiring any locking)
     50             // and can have getters which do not throw a RuntimeException if
     51             // the underlying Image is closed.
     52             mPixelStride = imagePlane.getPixelStride();
     53             mRowStride = imagePlane.getRowStride();
     54             mBuffer = imagePlane.getBuffer();
     55         }
     56 
     57         /**
     58          * @see {@link android.media.Image.Plane#getRowStride}
     59          */
     60         @Override
     61         public int getRowStride() {
     62             return mRowStride;
     63         }
     64 
     65         /**
     66          * @see {@link android.media.Image.Plane#getPixelStride}
     67          */
     68         @Override
     69         public int getPixelStride() {
     70             return mPixelStride;
     71         }
     72 
     73         /**
     74          * @see {@link android.media.Image.Plane#getBuffer}
     75          */
     76         @Override
     77         public ByteBuffer getBuffer() {
     78             return mBuffer;
     79         }
     80     }
     81 
     82     private final Object mLock;
     83     /**
     84      * {@link android.media.Image} is not thread-safe, so all interactions must
     85      * be guarded by {@link #mLock}.
     86      */
     87     @GuardedBy("mLock")
     88     private final android.media.Image mImage;
     89     private final int mFormat;
     90     private final int mWidth;
     91     private final int mHeight;
     92     private final long mTimestamp;
     93     private final ImmutableList<ImageProxy.Plane> mPlanes;
     94     @GuardedBy("mLock")
     95     private Rect mCropRect;
     96 
     97     public AndroidImageProxy(android.media.Image image) {
     98         mLock = new Object();
     99 
    100         mImage = image;
    101         // Copying out the contents of the Image means that this Image
    102         // implementation can be thread-safe (without requiring any locking)
    103         // and can have getters which do not throw a RuntimeException if
    104         // the underlying Image is closed.
    105         mFormat = mImage.getFormat();
    106         mWidth = mImage.getWidth();
    107         mHeight = mImage.getHeight();
    108         mTimestamp = mImage.getTimestamp();
    109 
    110         Image.Plane[] planes;
    111         planes = mImage.getPlanes();
    112         if (planes == null) {
    113             mPlanes = ImmutableList.of();
    114         } else {
    115             List<ImageProxy.Plane> wrappedPlanes = new ArrayList<>(planes.length);
    116             for (int i = 0; i < planes.length; i++) {
    117                 wrappedPlanes.add(new Plane(planes[i]));
    118             }
    119             mPlanes = ImmutableList.copyOf(wrappedPlanes);
    120         }
    121     }
    122 
    123     /**
    124      * @see {@link android.media.Image#getCropRect}
    125      */
    126     @Override
    127     public Rect getCropRect() {
    128         synchronized (mLock) {
    129             try {
    130                 mCropRect = mImage.getCropRect();
    131             } catch (IllegalStateException imageClosedException) {
    132                 // If the image is closed, then just return the cached CropRect.
    133                 return mCropRect;
    134             }
    135             return mCropRect;
    136         }
    137     }
    138 
    139     /**
    140      * @see {@link android.media.Image#setCropRect}
    141      */
    142     @Override
    143     public void setCropRect(Rect cropRect) {
    144         synchronized (mLock) {
    145             mCropRect = cropRect;
    146             try {
    147                 mImage.setCropRect(cropRect);
    148             } catch (IllegalStateException imageClosedException) {
    149                 // Ignore.
    150             }
    151         }
    152     }
    153 
    154     /**
    155      * @see {@link android.media.Image#getFormat}
    156      */
    157     @Override
    158     public int getFormat() {
    159         return mFormat;
    160     }
    161 
    162     /**
    163      * @see {@link android.media.Image#getHeight}
    164      */
    165     @Override
    166     public int getHeight() {
    167         return mHeight;
    168     }
    169 
    170     /**
    171      * @see {@link android.media.Image#getPlanes}
    172      */
    173     @Override
    174     public List<ImageProxy.Plane> getPlanes() {
    175         return mPlanes;
    176     }
    177 
    178     /**
    179      * @see {@link android.media.Image#getTimestamp}
    180      */
    181     @Override
    182     public long getTimestamp() {
    183         return mTimestamp;
    184     }
    185 
    186     /**
    187      * @see {@link android.media.Image#getWidth}
    188      */
    189     @Override
    190     public int getWidth() {
    191         return mWidth;
    192     }
    193 
    194     /**
    195      * @see {@link android.media.Image#close}
    196      */
    197     @Override
    198     public void close() {
    199         synchronized (mLock) {
    200             mImage.close();
    201         }
    202     }
    203 
    204     @Override
    205     public String toString() {
    206         return Objects.toStringHelper(this)
    207                 .add("format", getFormat())
    208                 .add("timestamp", getTimestamp())
    209                 .add("width", getWidth())
    210                 .add("height", getHeight())
    211                 .toString();
    212     }
    213 
    214     @Override
    215     public boolean equals(Object other) {
    216         if (other == null) {
    217             return false;
    218         }
    219         if (!(other instanceof ImageProxy)) {
    220             return false;
    221         }
    222         ImageProxy otherImage = (ImageProxy) other;
    223         return otherImage.getFormat() == getFormat() &&
    224                 otherImage.getWidth() == getWidth() &&
    225                 otherImage.getHeight() == getHeight() &&
    226                 otherImage.getTimestamp() == getTimestamp();
    227     }
    228 
    229     @Override
    230     public int hashCode() {
    231         return Objects.hashCode(getFormat(), getWidth(), getHeight(), getTimestamp());
    232     }
    233 }
    234