Home | History | Annotate | Download | only in filterfw
      1 /*
      2  * Copyright (C) 2012 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 android.annotation.TargetApi;
     20 import android.graphics.SurfaceTexture;
     21 import android.hardware.Camera;
     22 import android.hardware.Camera.CameraInfo;
     23 import android.hardware.Camera.PreviewCallback;
     24 import android.media.CamcorderProfile;
     25 import android.media.MediaRecorder;
     26 import android.opengl.GLES20;
     27 import android.os.Build.VERSION;
     28 import android.util.Log;
     29 import android.view.Display;
     30 import android.view.Surface;
     31 import android.view.SurfaceView;
     32 
     33 import java.io.IOException;
     34 import java.nio.ByteBuffer;
     35 import java.util.HashMap;
     36 import java.util.HashSet;
     37 import java.util.List;
     38 import java.util.Set;
     39 import java.util.Vector;
     40 import java.util.concurrent.LinkedBlockingQueue;
     41 import java.util.concurrent.TimeUnit;
     42 import java.util.concurrent.atomic.AtomicInteger;
     43 import java.util.concurrent.locks.Condition;
     44 import java.util.concurrent.locks.ReentrantLock;
     45 
     46 import javax.microedition.khronos.egl.EGLContext;
     47 
     48 /**
     49  * The CameraStreamer streams Frames from a camera to connected clients.
     50  *
     51  * There is one centralized CameraStreamer object per MffContext, and only one stream can be
     52  * active at any time. The CameraStreamer acts as a Camera "server" that streams frames to any
     53  * number of connected clients. Typically, these are CameraSource filters that are part of a
     54  * graph, but other clients can be written as well.
     55  */
     56 public class CameraStreamer {
     57 
     58     /** Camera Facing: Don't Care: Picks any available camera. */
     59     public static final int FACING_DONTCARE = 0;
     60     /** Camera Facing: Front: Use the front facing camera. */
     61     public static final int FACING_FRONT = 1;
     62     /** Camera Facing: Back: Use the rear facing camera. */
     63     public static final int FACING_BACK = 2;
     64 
     65     /** How long the streamer should wait to acquire the camera before giving up. */
     66     public static long MAX_CAMERA_WAIT_TIME = 5;
     67 
     68     /**
     69      * The global camera lock, that is closed when the camera is acquired by any CameraStreamer,
     70      * and opened when a streamer is done using the camera.
     71      */
     72     static ReentrantLock mCameraLock = new ReentrantLock();
     73 
     74     /** The Camera thread that grabs frames from the camera */
     75     private CameraRunnable mCameraRunner = null;
     76 
     77     private abstract class CamFrameHandler {
     78         protected int mCameraWidth;
     79         protected int mCameraHeight;
     80         protected int mOutWidth;
     81         protected int mOutHeight;
     82         protected CameraRunnable mRunner;
     83 
     84         /** Map of GLSL shaders (one for each target context) */
     85         protected HashMap<EGLContext, ImageShader> mTargetShaders
     86             = new HashMap<EGLContext, ImageShader>();
     87 
     88         /** Map of target textures (one for each target context) */
     89         protected HashMap<EGLContext, TextureSource> mTargetTextures
     90             = new HashMap<EGLContext, TextureSource>();
     91 
     92         /** Map of set of clients (one for each target context) */
     93         protected HashMap<EGLContext, Set<FrameClient>> mContextClients
     94             = new HashMap<EGLContext, Set<FrameClient>>();
     95 
     96         /** List of clients that are consuming camera frames. */
     97         protected Vector<FrameClient> mClients = new Vector<FrameClient>();
     98 
     99         public void initWithRunner(CameraRunnable camRunner) {
    100             mRunner = camRunner;
    101         }
    102 
    103         public void setCameraSize(int width, int height) {
    104             mCameraWidth = width;
    105             mCameraHeight = height;
    106         }
    107 
    108         public void registerClient(FrameClient client) {
    109             EGLContext context = RenderTarget.currentContext();
    110             Set<FrameClient> clientTargets = clientsForContext(context);
    111             clientTargets.add(client);
    112             mClients.add(client);
    113             onRegisterClient(client, context);
    114         }
    115 
    116         public void unregisterClient(FrameClient client) {
    117             EGLContext context = RenderTarget.currentContext();
    118             Set<FrameClient> clientTargets = clientsForContext(context);
    119             clientTargets.remove(client);
    120             if (clientTargets.isEmpty()) {
    121                 onCleanupContext(context);
    122             }
    123             mClients.remove(client);
    124         }
    125 
    126         public abstract void setupServerFrame();
    127         public abstract void updateServerFrame();
    128         public abstract void grabFrame(FrameImage2D targetFrame);
    129         public abstract void release();
    130 
    131         public void onUpdateCameraOrientation(int orientation) {
    132             if (orientation % 180 != 0) {
    133                 mOutWidth = mCameraHeight;
    134                 mOutHeight = mCameraWidth;
    135             } else {
    136                 mOutWidth = mCameraWidth;
    137                 mOutHeight = mCameraHeight;
    138             }
    139         }
    140 
    141         protected Set<FrameClient> clientsForContext(EGLContext context) {
    142             Set<FrameClient> clients = mContextClients.get(context);
    143             if (clients == null) {
    144                 clients = new HashSet<FrameClient>();
    145                 mContextClients.put(context, clients);
    146             }
    147             return clients;
    148         }
    149 
    150         protected void onRegisterClient(FrameClient client, EGLContext context) {
    151         }
    152 
    153         protected void onCleanupContext(EGLContext context) {
    154             TextureSource texture = mTargetTextures.get(context);
    155             ImageShader shader = mTargetShaders.get(context);
    156             if (texture != null) {
    157                 texture.release();
    158                 mTargetTextures.remove(context);
    159             }
    160             if (shader != null) {
    161                 mTargetShaders.remove(context);
    162             }
    163         }
    164 
    165         protected TextureSource textureForContext(EGLContext context) {
    166             TextureSource texture = mTargetTextures.get(context);
    167             if (texture == null) {
    168                 texture = createClientTexture();
    169                 mTargetTextures.put(context, texture);
    170             }
    171             return texture;
    172         }
    173 
    174         protected ImageShader shaderForContext(EGLContext context) {
    175             ImageShader shader = mTargetShaders.get(context);
    176             if (shader == null) {
    177                 shader = createClientShader();
    178                 mTargetShaders.put(context, shader);
    179             }
    180             return shader;
    181         }
    182 
    183         protected ImageShader createClientShader() {
    184             return null;
    185         }
    186 
    187         protected TextureSource createClientTexture() {
    188             return null;
    189         }
    190 
    191         public boolean isFrontMirrored() {
    192             return true;
    193         }
    194     }
    195 
    196     // Jellybean (and later) back-end
    197     @TargetApi(16)
    198     private class CamFrameHandlerJB extends CamFrameHandlerICS {
    199 
    200         @Override
    201         public void setupServerFrame() {
    202             setupPreviewTexture(mRunner.mCamera);
    203         }
    204 
    205         @Override
    206         public synchronized void updateServerFrame() {
    207             updateSurfaceTexture();
    208             informClients();
    209         }
    210 
    211         @Override
    212         public synchronized void grabFrame(FrameImage2D targetFrame) {
    213             TextureSource targetTex = TextureSource.newExternalTexture();
    214             ImageShader copyShader = shaderForContext(RenderTarget.currentContext());
    215             if (targetTex == null || copyShader == null) {
    216                 throw new RuntimeException("Attempting to grab camera frame from unknown "
    217                     + "thread: " + Thread.currentThread() + "!");
    218             }
    219             mPreviewSurfaceTexture.attachToGLContext(targetTex.getTextureId());
    220             updateTransform(copyShader);
    221             updateShaderTargetRect(copyShader);
    222             targetFrame.resize(new int[] { mOutWidth, mOutHeight });
    223             copyShader.process(targetTex,
    224                                targetFrame.lockRenderTarget(),
    225                                mOutWidth,
    226                                mOutHeight);
    227             targetFrame.setTimestamp(mPreviewSurfaceTexture.getTimestamp());
    228             targetFrame.unlock();
    229             mPreviewSurfaceTexture.detachFromGLContext();
    230             targetTex.release();
    231         }
    232 
    233         @Override
    234         protected void updateShaderTargetRect(ImageShader shader) {
    235             if ((mRunner.mActualFacing == FACING_FRONT) && mRunner.mFlipFront) {
    236                 shader.setTargetRect(1f, 1f, -1f, -1f);
    237             } else {
    238                 shader.setTargetRect(0f, 1f, 1f, -1f);
    239             }
    240         }
    241 
    242         @Override
    243         protected void setupPreviewTexture(Camera camera) {
    244             super.setupPreviewTexture(camera);
    245             mPreviewSurfaceTexture.detachFromGLContext();
    246         }
    247 
    248         protected void updateSurfaceTexture() {
    249             mPreviewSurfaceTexture.attachToGLContext(mPreviewTexture.getTextureId());
    250             mPreviewSurfaceTexture.updateTexImage();
    251             mPreviewSurfaceTexture.detachFromGLContext();
    252         }
    253 
    254         protected void informClients() {
    255             synchronized (mClients) {
    256                 for (FrameClient client : mClients) {
    257                     client.onCameraFrameAvailable();
    258                 }
    259             }
    260         }
    261     }
    262 
    263     // ICS (and later) back-end
    264     @TargetApi(15)
    265     private class CamFrameHandlerICS extends CamFrameHandler  {
    266 
    267         protected static final String mCopyShaderSource =
    268             "#extension GL_OES_EGL_image_external : require\n" +
    269             "precision mediump float;\n" +
    270             "uniform samplerExternalOES tex_sampler_0;\n" +
    271             "varying vec2 v_texcoord;\n" +
    272             "void main() {\n" +
    273             "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
    274             "}\n";
    275 
    276         /** The camera transform matrix */
    277         private float[] mCameraTransform = new float[16];
    278 
    279         /** The texture the camera streams to */
    280         protected TextureSource mPreviewTexture = null;
    281         protected SurfaceTexture mPreviewSurfaceTexture = null;
    282 
    283         /** Map of target surface textures (one for each target context) */
    284         protected HashMap<EGLContext, SurfaceTexture> mTargetSurfaceTextures
    285             = new HashMap<EGLContext, SurfaceTexture>();
    286 
    287         /** Map of RenderTargets for client SurfaceTextures */
    288         protected HashMap<SurfaceTexture, RenderTarget> mClientRenderTargets
    289             = new HashMap<SurfaceTexture, RenderTarget>();
    290 
    291         /** Server side copy shader */
    292         protected ImageShader mCopyShader = null;
    293 
    294         @Override
    295         public synchronized void setupServerFrame() {
    296             setupPreviewTexture(mRunner.mCamera);
    297         }
    298 
    299         @Override
    300         public synchronized void updateServerFrame() {
    301             mPreviewSurfaceTexture.updateTexImage();
    302             distributeFrames();
    303         }
    304 
    305         @Override
    306         public void onUpdateCameraOrientation(int orientation) {
    307             super.onUpdateCameraOrientation(orientation);
    308             mRunner.mCamera.setDisplayOrientation(orientation);
    309             updateSurfaceTextureSizes();
    310         }
    311 
    312         @Override
    313         public synchronized void onRegisterClient(FrameClient client, EGLContext context) {
    314             final Set<FrameClient> clientTargets = clientsForContext(context);
    315 
    316             // Make sure we have texture, shader, and surfacetexture setup for this context.
    317             TextureSource clientTex = textureForContext(context);
    318             ImageShader copyShader = shaderForContext(context);
    319             SurfaceTexture surfTex = surfaceTextureForContext(context);
    320 
    321             // Listen to client-side surface texture updates
    322             surfTex.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
    323                 @Override
    324                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
    325                     for (FrameClient clientTarget : clientTargets) {
    326                         clientTarget.onCameraFrameAvailable();
    327                     }
    328                 }
    329             });
    330         }
    331 
    332         @Override
    333         public synchronized void grabFrame(FrameImage2D targetFrame) {
    334             // Get the GL objects for the receiver's context
    335             EGLContext clientContext = RenderTarget.currentContext();
    336             TextureSource clientTex = textureForContext(clientContext);
    337             ImageShader copyShader = shaderForContext(clientContext);
    338             SurfaceTexture surfTex = surfaceTextureForContext(clientContext);
    339             if (clientTex == null || copyShader == null || surfTex == null) {
    340                 throw new RuntimeException("Attempting to grab camera frame from unknown "
    341                     + "thread: " + Thread.currentThread() + "!");
    342             }
    343 
    344             // Copy from client ST to client tex
    345             surfTex.updateTexImage();
    346             targetFrame.resize(new int[] { mOutWidth, mOutHeight });
    347             copyShader.process(clientTex,
    348                                targetFrame.lockRenderTarget(),
    349                                mOutWidth,
    350                                mOutHeight);
    351 
    352             targetFrame.setTimestamp(mPreviewSurfaceTexture.getTimestamp());
    353             targetFrame.unlock();
    354         }
    355 
    356         @Override
    357         public synchronized void release() {
    358             if (mPreviewTexture != null) {
    359                 mPreviewTexture.release();
    360                 mPreviewTexture = null;
    361             }
    362             if (mPreviewSurfaceTexture != null) {
    363                 mPreviewSurfaceTexture.release();
    364                 mPreviewSurfaceTexture = null;
    365             }
    366         }
    367 
    368         @Override
    369         protected ImageShader createClientShader() {
    370             return new ImageShader(mCopyShaderSource);
    371         }
    372 
    373         @Override
    374         protected TextureSource createClientTexture() {
    375             return TextureSource.newExternalTexture();
    376         }
    377 
    378         protected void distributeFrames() {
    379             updateTransform(getCopyShader());
    380             updateShaderTargetRect(getCopyShader());
    381 
    382             for (SurfaceTexture clientTexture : mTargetSurfaceTextures.values()) {
    383                 RenderTarget clientTarget = renderTargetFor(clientTexture);
    384                 clientTarget.focus();
    385                 getCopyShader().process(mPreviewTexture,
    386                                         clientTarget,
    387                                         mOutWidth,
    388                                         mOutHeight);
    389                 GLToolbox.checkGlError("distribute frames");
    390                 clientTarget.swapBuffers();
    391             }
    392         }
    393 
    394         protected RenderTarget renderTargetFor(SurfaceTexture surfaceTex) {
    395             RenderTarget target = mClientRenderTargets.get(surfaceTex);
    396             if (target == null) {
    397                 target = RenderTarget.currentTarget().forSurfaceTexture(surfaceTex);
    398                 mClientRenderTargets.put(surfaceTex, target);
    399             }
    400             return target;
    401         }
    402 
    403         protected void setupPreviewTexture(Camera camera) {
    404             if (mPreviewTexture == null) {
    405                 mPreviewTexture = TextureSource.newExternalTexture();
    406             }
    407             if (mPreviewSurfaceTexture == null) {
    408                 mPreviewSurfaceTexture = new SurfaceTexture(mPreviewTexture.getTextureId());
    409                 try {
    410                     camera.setPreviewTexture(mPreviewSurfaceTexture);
    411                 } catch (IOException e) {
    412                     throw new RuntimeException("Could not bind camera surface texture: " +
    413                                                e.getMessage() + "!");
    414                 }
    415                 mPreviewSurfaceTexture.setOnFrameAvailableListener(mOnCameraFrameListener);
    416             }
    417         }
    418 
    419         protected ImageShader getCopyShader() {
    420             if (mCopyShader == null) {
    421                 mCopyShader = new ImageShader(mCopyShaderSource);
    422             }
    423             return mCopyShader;
    424         }
    425 
    426         protected SurfaceTexture surfaceTextureForContext(EGLContext context) {
    427             SurfaceTexture surfTex = mTargetSurfaceTextures.get(context);
    428             if (surfTex == null) {
    429                 TextureSource texture = textureForContext(context);
    430                 if (texture != null) {
    431                     surfTex = new SurfaceTexture(texture.getTextureId());
    432                     surfTex.setDefaultBufferSize(mOutWidth, mOutHeight);
    433                     mTargetSurfaceTextures.put(context, surfTex);
    434                 }
    435             }
    436             return surfTex;
    437         }
    438 
    439         protected void updateShaderTargetRect(ImageShader shader) {
    440             if ((mRunner.mActualFacing == FACING_FRONT) && mRunner.mFlipFront) {
    441                 shader.setTargetRect(1f, 0f, -1f, 1f);
    442             } else {
    443                 shader.setTargetRect(0f, 0f, 1f, 1f);
    444             }
    445         }
    446 
    447         protected synchronized void updateSurfaceTextureSizes() {
    448             for (SurfaceTexture clientTexture : mTargetSurfaceTextures.values()) {
    449                 clientTexture.setDefaultBufferSize(mOutWidth, mOutHeight);
    450             }
    451         }
    452 
    453         protected void updateTransform(ImageShader shader) {
    454             mPreviewSurfaceTexture.getTransformMatrix(mCameraTransform);
    455             shader.setSourceTransform(mCameraTransform);
    456         }
    457 
    458         @Override
    459         protected void onCleanupContext(EGLContext context) {
    460             super.onCleanupContext(context);
    461             SurfaceTexture surfaceTex = mTargetSurfaceTextures.get(context);
    462             if (surfaceTex != null) {
    463                 surfaceTex.release();
    464                 mTargetSurfaceTextures.remove(context);
    465             }
    466         }
    467 
    468         protected SurfaceTexture.OnFrameAvailableListener mOnCameraFrameListener =
    469                 new SurfaceTexture.OnFrameAvailableListener() {
    470             @Override
    471             public void onFrameAvailable(SurfaceTexture surfaceTexture) {
    472                 mRunner.signalNewFrame();
    473             }
    474         };
    475     }
    476 
    477     // Gingerbread (and later) back-end
    478     @TargetApi(9)
    479     private final class CamFrameHandlerGB extends CamFrameHandler  {
    480 
    481         private SurfaceView mSurfaceView;
    482         private byte[] mFrameBufferFront;
    483         private byte[] mFrameBufferBack;
    484         private boolean mWriteToBack = true;
    485         private float[] mTargetCoords = new float[] { 0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f };
    486         final Object mBufferLock = new Object();
    487 
    488         private String mNV21ToRGBAFragment =
    489             "precision mediump float;\n" +
    490             "\n" +
    491             "uniform sampler2D tex_sampler_0;\n" +
    492             "varying vec2 v_y_texcoord;\n" +
    493             "varying vec2 v_vu_texcoord;\n" +
    494             "varying vec2 v_pixcoord;\n" +
    495             "\n" +
    496             "vec3 select(vec4 yyyy, vec4 vuvu, int s) {\n" +
    497             "  if (s == 0) {\n" +
    498             "    return vec3(yyyy.r, vuvu.g, vuvu.r);\n" +
    499             "  } else if (s == 1) {\n" +
    500             "    return vec3(yyyy.g, vuvu.g, vuvu.r);\n" +
    501             " } else if (s == 2) {\n" +
    502             "    return vec3(yyyy.b, vuvu.a, vuvu.b);\n" +
    503             "  } else  {\n" +
    504             "    return vec3(yyyy.a, vuvu.a, vuvu.b);\n" +
    505             "  }\n" +
    506             "}\n" +
    507             "\n" +
    508             "vec3 yuv2rgb(vec3 yuv) {\n" +
    509             "  mat4 conversion = mat4(1.0,  0.0,    1.402, -0.701,\n" +
    510             "                         1.0, -0.344, -0.714,  0.529,\n" +
    511             "                         1.0,  1.772,  0.0,   -0.886,\n" +
    512             "                         0, 0, 0, 0);" +
    513             "  return (vec4(yuv, 1.0) * conversion).rgb;\n" +
    514             "}\n" +
    515             "\n" +
    516             "void main() {\n" +
    517             "  vec4 yyyy = texture2D(tex_sampler_0, v_y_texcoord);\n" +
    518             "  vec4 vuvu = texture2D(tex_sampler_0, v_vu_texcoord);\n" +
    519             "  int s = int(mod(floor(v_pixcoord.x), 4.0));\n" +
    520             "  vec3 yuv = select(yyyy, vuvu, s);\n" +
    521             "  vec3 rgb = yuv2rgb(yuv);\n" +
    522             "  gl_FragColor = vec4(rgb, 1.0);\n" +
    523             "}";
    524 
    525         private String mNV21ToRGBAVertex =
    526             "attribute vec4 a_position;\n" +
    527             "attribute vec2 a_y_texcoord;\n" +
    528             "attribute vec2 a_vu_texcoord;\n" +
    529             "attribute vec2 a_pixcoord;\n" +
    530             "varying vec2 v_y_texcoord;\n" +
    531             "varying vec2 v_vu_texcoord;\n" +
    532             "varying vec2 v_pixcoord;\n" +
    533             "void main() {\n" +
    534             "  gl_Position = a_position;\n" +
    535             "  v_y_texcoord = a_y_texcoord;\n" +
    536             "  v_vu_texcoord = a_vu_texcoord;\n" +
    537             "  v_pixcoord = a_pixcoord;\n" +
    538             "}\n";
    539 
    540         private byte[] readBuffer() {
    541             synchronized (mBufferLock) {
    542                 return mWriteToBack ? mFrameBufferFront : mFrameBufferBack;
    543             }
    544         }
    545 
    546         private byte[] writeBuffer() {
    547             synchronized (mBufferLock) {
    548                 return mWriteToBack ? mFrameBufferBack : mFrameBufferFront;
    549             }
    550         }
    551 
    552         private synchronized void swapBuffers() {
    553             synchronized (mBufferLock) {
    554                 mWriteToBack = !mWriteToBack;
    555             }
    556         }
    557 
    558         private PreviewCallback mPreviewCallback = new PreviewCallback() {
    559 
    560             @Override
    561             public void onPreviewFrame(byte[] data, Camera camera) {
    562                 swapBuffers();
    563                 camera.addCallbackBuffer(writeBuffer());
    564                 mRunner.signalNewFrame();
    565             }
    566 
    567         };
    568 
    569         @Override
    570         public void setupServerFrame() {
    571             checkCameraDimensions();
    572             Camera camera = mRunner.mCamera;
    573             int bufferSize = mCameraWidth * (mCameraHeight + mCameraHeight/2);
    574             mFrameBufferFront = new byte[bufferSize];
    575             mFrameBufferBack = new byte[bufferSize];
    576             camera.addCallbackBuffer(writeBuffer());
    577             camera.setPreviewCallbackWithBuffer(mPreviewCallback);
    578             SurfaceView previewDisplay = getPreviewDisplay();
    579             if (previewDisplay != null) {
    580                 try {
    581                     camera.setPreviewDisplay(previewDisplay.getHolder());
    582                 } catch (IOException e) {
    583                     throw new RuntimeException("Could not start camera with given preview " +
    584                             "display!");
    585                 }
    586             }
    587         }
    588 
    589         private void checkCameraDimensions() {
    590             if (mCameraWidth % 4 != 0) {
    591                 throw new RuntimeException("Camera width must be a multiple of 4!");
    592             } else if (mCameraHeight % 2 != 0) {
    593                 throw new RuntimeException("Camera height must be a multiple of 2!");
    594             }
    595         }
    596 
    597         @Override
    598         public void updateServerFrame() {
    599             // Server frame has been updated already, simply inform clients here.
    600             informClients();
    601         }
    602 
    603         @Override
    604         public void grabFrame(FrameImage2D targetFrame) {
    605             EGLContext clientContext = RenderTarget.currentContext();
    606 
    607             // Copy camera data to the client YUV texture
    608             TextureSource clientTex = textureForContext(clientContext);
    609             int texWidth = mCameraWidth / 4;
    610             int texHeight = mCameraHeight + mCameraHeight / 2;
    611             synchronized(mBufferLock) {    // Don't swap buffers while we are reading
    612                 ByteBuffer pixels = ByteBuffer.wrap(readBuffer());
    613                 clientTex.allocateWithPixels(pixels, texWidth, texHeight);
    614             }
    615             clientTex.setParameter(GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    616             clientTex.setParameter(GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    617 
    618             // Setup the YUV-2-RGBA shader
    619             ImageShader transferShader = shaderForContext(clientContext);
    620             transferShader.setTargetCoords(mTargetCoords);
    621             updateShaderPixelSize(transferShader);
    622 
    623             // Convert pixels into target frame
    624             targetFrame.resize(new int[] { mOutWidth, mOutHeight });
    625             transferShader.process(clientTex,
    626                     targetFrame.lockRenderTarget(),
    627                     mOutWidth,
    628                     mOutHeight);
    629             targetFrame.unlock();
    630         }
    631 
    632         @Override
    633         public void onUpdateCameraOrientation(int orientation) {
    634             super.onUpdateCameraOrientation(orientation);
    635             if ((mRunner.mActualFacing == FACING_FRONT) && mRunner.mFlipFront) {
    636                 switch (orientation) {
    637                     case 0:
    638                         mTargetCoords = new float[] { 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f };
    639                         break;
    640                     case 90:
    641                         mTargetCoords = new float[] { 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f };
    642                         break;
    643                     case 180:
    644                         mTargetCoords = new float[] { 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f };
    645                         break;
    646                     case 270:
    647                         mTargetCoords = new float[] { 1f, 1f, 1f, 0f, 0f, 1f, 0f, 0f };
    648                         break;
    649                 }
    650             } else {
    651                 switch (orientation) {
    652                     case 0:
    653                         mTargetCoords = new float[] { 0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f };
    654                         break;
    655                     case 90:
    656                         mTargetCoords = new float[] { 1f, 0f, 1f, 1f, 0f, 0f, 0f, 1f };
    657                         break;
    658                     case 180:
    659                         mTargetCoords = new float[] { 1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f };
    660                         break;
    661                     case 270:
    662                         mTargetCoords = new float[] { 0f, 1f, 0f, 0f, 1f, 1f, 1f, 0f };
    663                         break;
    664                 }
    665             }
    666         }
    667 
    668         @Override
    669         public void release() {
    670             mFrameBufferBack = null;
    671             mFrameBufferFront = null;
    672         }
    673 
    674         @Override
    675         public boolean isFrontMirrored() {
    676             return false;
    677         }
    678 
    679         @Override
    680         protected ImageShader createClientShader() {
    681             ImageShader shader = new ImageShader(mNV21ToRGBAVertex, mNV21ToRGBAFragment);
    682             // TODO: Make this a VBO
    683             float[] yCoords = new float[] {
    684                     0f, 0f,
    685                     1f, 0f,
    686                     0f, 2f / 3f,
    687                     1f, 2f / 3f };
    688             float[] uvCoords = new float[] {
    689                     0f, 2f / 3f,
    690                     1f, 2f / 3f,
    691                     0f, 1f,
    692                     1f, 1f };
    693             shader.setAttributeValues("a_y_texcoord", yCoords, 2);
    694             shader.setAttributeValues("a_vu_texcoord", uvCoords, 2);
    695             return shader;
    696         }
    697 
    698         @Override
    699         protected TextureSource createClientTexture() {
    700             TextureSource texture = TextureSource.newTexture();
    701             texture.setParameter(GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    702             texture.setParameter(GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    703             return texture;
    704         }
    705 
    706         private void updateShaderPixelSize(ImageShader shader) {
    707             float[] pixCoords = new float[] {
    708                     0f, 0f,
    709                     mCameraWidth, 0f,
    710                     0f, mCameraHeight,
    711                     mCameraWidth, mCameraHeight };
    712             shader.setAttributeValues("a_pixcoord", pixCoords, 2);
    713         }
    714 
    715         private SurfaceView getPreviewDisplay() {
    716             if (mSurfaceView == null) {
    717                 mSurfaceView = mRunner.getContext().getDummySurfaceView();
    718             }
    719             return mSurfaceView;
    720         }
    721 
    722         private void informClients() {
    723             synchronized (mClients) {
    724                 for (FrameClient client : mClients) {
    725                     client.onCameraFrameAvailable();
    726                 }
    727             }
    728         }
    729     }
    730 
    731     private static class State {
    732         public static final int STATE_RUNNING = 1;
    733         public static final int STATE_STOPPED = 2;
    734         public static final int STATE_HALTED = 3;
    735 
    736         private AtomicInteger mCurrent = new AtomicInteger(STATE_STOPPED);
    737 
    738         public int current() {
    739             return mCurrent.get();
    740         }
    741 
    742         public void set(int newState) {
    743             mCurrent.set(newState);
    744         }
    745     }
    746 
    747     private static class Event {
    748         public static final int START = 1;
    749         public static final int FRAME = 2;
    750         public static final int STOP = 3;
    751         public static final int HALT = 4;
    752         public static final int RESTART = 5;
    753         public static final int UPDATE = 6;
    754         public static final int TEARDOWN = 7;
    755 
    756         public int code;
    757 
    758         public Event(int code) {
    759             this.code = code;
    760         }
    761     }
    762 
    763     private final class CameraRunnable implements Runnable {
    764 
    765         /** On slower devices the event queue can easily fill up. We bound the queue to this. */
    766         private final static int MAX_EVENTS = 32;
    767 
    768         /** The runner's state */
    769         private State mState = new State();
    770 
    771         /** The CameraRunner's event queue */
    772         private LinkedBlockingQueue<Event> mEventQueue = new LinkedBlockingQueue<Event>(MAX_EVENTS);
    773 
    774         /** The requested FPS */
    775         private int mRequestedFramesPerSec = 30;
    776 
    777         /** The actual FPS */
    778         private int mActualFramesPerSec = 0;
    779 
    780         /** The requested preview width and height */
    781         private int mRequestedPreviewWidth = 640;
    782         private int mRequestedPreviewHeight = 480;
    783 
    784         /** The requested picture width and height */
    785         private int mRequestedPictureWidth = 640;
    786         private int mRequestedPictureHeight = 480;
    787 
    788         /** The actual camera width and height */
    789         private int[] mActualDims = null;
    790 
    791         /** The requested facing */
    792         private int mRequestedFacing = FACING_DONTCARE;
    793 
    794         /** The actual facing */
    795         private int mActualFacing = FACING_DONTCARE;
    796 
    797         /** Whether to horizontally flip the front facing camera */
    798         private boolean mFlipFront = true;
    799 
    800         /** The display the camera streamer is bound to. */
    801         private Display mDisplay = null;
    802 
    803         /** The camera and screen orientation. */
    804         private int mCamOrientation = 0;
    805         private int mOrientation = -1;
    806 
    807         /** The camera rotation (used for capture). */
    808         private int mCamRotation = 0;
    809 
    810         /** The camera flash mode */
    811         private String mFlashMode = Camera.Parameters.FLASH_MODE_OFF;
    812 
    813         /** The camera object */
    814         private Camera mCamera = null;
    815 
    816         private MediaRecorder mRecorder = null;
    817 
    818         /** The ID of the currently used camera */
    819         int mCamId = 0;
    820 
    821         /** The platform-dependent camera frame handler. */
    822         private CamFrameHandler mCamFrameHandler = null;
    823 
    824         /** The set of camera listeners. */
    825         private Set<CameraListener> mCamListeners = new HashSet<CameraListener>();
    826 
    827         private ReentrantLock mCameraReadyLock = new ReentrantLock(true);
    828         // mCameraReady condition is used when waiting for the camera getting ready.
    829         private Condition mCameraReady = mCameraReadyLock.newCondition();
    830         // external camera lock used to provide the capability of external camera access.
    831         private ExternalCameraLock mExternalCameraLock = new ExternalCameraLock();
    832 
    833         private RenderTarget mRenderTarget;
    834         private MffContext mContext;
    835 
    836         /**
    837          *  This provides the capability of locking and unlocking from different threads.
    838          *  The thread will wait until the lock state is idle. Any thread can wake up
    839          *  a waiting thread by calling unlock (i.e. signal), provided that unlock
    840          *  are called using the same context when lock was called. Using context prevents
    841          *  from rogue usage of unlock.
    842          */
    843         private class ExternalCameraLock {
    844             public static final int IDLE = 0;
    845             public static final int IN_USE = 1;
    846             private int mLockState = IDLE;
    847             private Object mLockContext;
    848             private final ReentrantLock mLock = new ReentrantLock(true);
    849             private final Condition mInUseLockCondition= mLock.newCondition();
    850 
    851             public boolean lock(Object context) {
    852                 if (context == null) {
    853                     throw new RuntimeException("Null context when locking");
    854                 }
    855                 mLock.lock();
    856                 if (mLockState == IN_USE) {
    857                     try {
    858                         mInUseLockCondition.await();
    859                     } catch (InterruptedException e) {
    860                         return false;
    861                     }
    862                 }
    863                 mLockState = IN_USE;
    864                 mLockContext = context;
    865                 mLock.unlock();
    866                 return true;
    867             }
    868 
    869             public void unlock(Object context) {
    870                 mLock.lock();
    871                 if (mLockState != IN_USE) {
    872                     throw new RuntimeException("Not in IN_USE state");
    873                 }
    874                 if (context != mLockContext) {
    875                     throw new RuntimeException("Lock is not owned by this context");
    876                 }
    877                 mLockState = IDLE;
    878                 mLockContext = null;
    879                 mInUseLockCondition.signal();
    880                 mLock.unlock();
    881             }
    882         }
    883 
    884         public CameraRunnable(MffContext context) {
    885             mContext = context;
    886             createCamFrameHandler();
    887             mCamFrameHandler.initWithRunner(this);
    888             launchThread();
    889         }
    890 
    891         public MffContext getContext() {
    892             return mContext;
    893         }
    894 
    895         public void loop() {
    896             while (true) {
    897                 try {
    898                     Event event = nextEvent();
    899                     if (event == null) continue;
    900                     switch (event.code) {
    901                         case Event.START:
    902                             onStart();
    903                             break;
    904                         case Event.STOP:
    905                             onStop();
    906                             break;
    907                         case Event.FRAME:
    908                             onFrame();
    909                             break;
    910                         case Event.HALT:
    911                             onHalt();
    912                             break;
    913                         case Event.RESTART:
    914                             onRestart();
    915                             break;
    916                         case Event.UPDATE:
    917                             onUpdate();
    918                             break;
    919                         case Event.TEARDOWN:
    920                             onTearDown();
    921                             break;
    922                     }
    923                 } catch (Exception e) {
    924                     e.printStackTrace();
    925                 }
    926             }
    927         }
    928 
    929         @Override
    930         public void run() {
    931             loop();
    932         }
    933 
    934         public void signalNewFrame() {
    935             pushEvent(Event.FRAME, false);
    936         }
    937 
    938         public void pushEvent(int eventId, boolean required) {
    939             try {
    940                 if (required) {
    941                     mEventQueue.put(new Event(eventId));
    942                 } else {
    943                     mEventQueue.offer(new Event(eventId));
    944                 }
    945             } catch (InterruptedException e) {
    946                 // We should never get here (as we do not limit capacity in the queue), but if
    947                 // we do, we log an error.
    948                 Log.e("CameraStreamer", "Dropping event " + eventId + "!");
    949             }
    950         }
    951 
    952         public void launchThread() {
    953             Thread cameraThread = new Thread(this);
    954             cameraThread.start();
    955         }
    956 
    957         @Deprecated
    958         public Camera getCamera() {
    959             synchronized (mState) {
    960                 return mCamera;
    961             }
    962         }
    963 
    964         public Camera lockCamera(Object context) {
    965             mExternalCameraLock.lock(context);
    966             /**
    967              * since lockCamera can happen right after closeCamera,
    968              * the camera handle can be null, wait until valid handle
    969              * is acquired.
    970              */
    971             while (mCamera == null) {
    972                 mExternalCameraLock.unlock(context);
    973                 mCameraReadyLock.lock();
    974                 try {
    975                     mCameraReady.await();
    976                 } catch (InterruptedException e) {
    977                     throw new RuntimeException("Condition interrupted", e);
    978                 }
    979                 mCameraReadyLock.unlock();
    980                 mExternalCameraLock.lock(context);
    981             }
    982             return mCamera;
    983         }
    984 
    985         public void unlockCamera(Object context) {
    986             mExternalCameraLock.unlock(context);
    987         }
    988 
    989         public int getCurrentCameraId() {
    990             synchronized (mState) {
    991                 return mCamId;
    992             }
    993         }
    994 
    995         public boolean isRunning() {
    996             return mState.current() != State.STATE_STOPPED;
    997         }
    998 
    999         public void addListener(CameraListener listener) {
   1000             synchronized (mCamListeners) {
   1001                 mCamListeners.add(listener);
   1002             }
   1003         }
   1004 
   1005         public void removeListener(CameraListener listener) {
   1006             synchronized (mCamListeners) {
   1007                 mCamListeners.remove(listener);
   1008             }
   1009         }
   1010 
   1011         public synchronized void bindToDisplay(Display display) {
   1012             mDisplay = display;
   1013         }
   1014 
   1015         public synchronized void setDesiredPreviewSize(int width, int height) {
   1016             if (width != mRequestedPreviewWidth || height != mRequestedPreviewHeight) {
   1017                 mRequestedPreviewWidth = width;
   1018                 mRequestedPreviewHeight = height;
   1019                 onParamsUpdated();
   1020             }
   1021         }
   1022 
   1023         public synchronized void setDesiredPictureSize(int width, int height) {
   1024             if (width != mRequestedPictureWidth || height != mRequestedPictureHeight) {
   1025                 mRequestedPictureWidth = width;
   1026                 mRequestedPictureHeight = height;
   1027                 onParamsUpdated();
   1028             }
   1029         }
   1030 
   1031         public synchronized void setDesiredFrameRate(int fps) {
   1032             if (fps != mRequestedFramesPerSec) {
   1033                 mRequestedFramesPerSec = fps;
   1034                 onParamsUpdated();
   1035             }
   1036         }
   1037 
   1038         public synchronized void setFacing(int facing) {
   1039             if (facing != mRequestedFacing) {
   1040                 switch (facing) {
   1041                     case FACING_DONTCARE:
   1042                     case FACING_FRONT:
   1043                     case FACING_BACK:
   1044                         mRequestedFacing = facing;
   1045                         break;
   1046                     default:
   1047                         throw new IllegalArgumentException("Unknown facing value '" + facing
   1048                             + "' passed to setFacing!");
   1049                 }
   1050                 onParamsUpdated();
   1051             }
   1052         }
   1053 
   1054         public synchronized void setFlipFrontCamera(boolean flipFront) {
   1055             if (mFlipFront != flipFront) {
   1056                 mFlipFront = flipFront;
   1057             }
   1058         }
   1059 
   1060         public synchronized void setFlashMode(String flashMode) {
   1061             if (!flashMode.equals(mFlashMode)) {
   1062                 mFlashMode = flashMode;
   1063                 onParamsUpdated();
   1064             }
   1065         }
   1066 
   1067         public synchronized int getCameraFacing() {
   1068             return mActualFacing;
   1069         }
   1070 
   1071         public synchronized int getCameraRotation() {
   1072             return mCamRotation;
   1073         }
   1074 
   1075         public synchronized boolean supportsHardwareFaceDetection() {
   1076             //return mCamFrameHandler.supportsHardwareFaceDetection();
   1077             // TODO
   1078             return true;
   1079         }
   1080 
   1081         public synchronized int getCameraWidth() {
   1082             return (mActualDims != null) ? mActualDims[0] : 0;
   1083         }
   1084 
   1085         public synchronized int getCameraHeight() {
   1086             return (mActualDims != null) ? mActualDims[1] : 0;
   1087         }
   1088 
   1089         public synchronized int getCameraFrameRate() {
   1090             return mActualFramesPerSec;
   1091         }
   1092 
   1093         public synchronized String getFlashMode() {
   1094             return mCamera.getParameters().getFlashMode();
   1095         }
   1096 
   1097         public synchronized boolean canStart() {
   1098             // If we can get a camera id without error we should be able to start.
   1099             try {
   1100                 getCameraId();
   1101             } catch (RuntimeException e) {
   1102                 return false;
   1103             }
   1104             return true;
   1105         }
   1106 
   1107         public boolean grabFrame(FrameImage2D targetFrame) {
   1108             // Make sure we stay in state running while we are grabbing the frame.
   1109             synchronized (mState) {
   1110                 if (mState.current() != State.STATE_RUNNING) {
   1111                     return false;
   1112                 }
   1113                 // we may not have the camera ready, this might happen when in the middle
   1114                 // of switching camera.
   1115                 if (mCamera == null) {
   1116                     return false;
   1117                 }
   1118                 mCamFrameHandler.grabFrame(targetFrame);
   1119                 return true;
   1120             }
   1121         }
   1122 
   1123         public CamFrameHandler getCamFrameHandler() {
   1124             return mCamFrameHandler;
   1125         }
   1126 
   1127         private void onParamsUpdated() {
   1128             pushEvent(Event.UPDATE, true);
   1129         }
   1130 
   1131         private Event nextEvent() {
   1132             try {
   1133                 return mEventQueue.take();
   1134             } catch (InterruptedException e) {
   1135                 // Ignore and keep going.
   1136                 Log.w("GraphRunner", "Event queue processing was interrupted.");
   1137                 return null;
   1138             }
   1139         }
   1140 
   1141         private void onStart() {
   1142             if (mState.current() == State.STATE_STOPPED) {
   1143                 mState.set(State.STATE_RUNNING);
   1144                 getRenderTarget().focus();
   1145                 openCamera();
   1146             }
   1147         }
   1148 
   1149         private void onStop() {
   1150             if (mState.current() == State.STATE_RUNNING) {
   1151                 closeCamera();
   1152                 RenderTarget.focusNone();
   1153             }
   1154             // Set state to stop (halted becomes stopped).
   1155             mState.set(State.STATE_STOPPED);
   1156         }
   1157 
   1158         private void onHalt() {
   1159             // Only halt if running. Stopped overrides halt.
   1160             if (mState.current() == State.STATE_RUNNING) {
   1161                 closeCamera();
   1162                 RenderTarget.focusNone();
   1163                 mState.set(State.STATE_HALTED);
   1164             }
   1165         }
   1166 
   1167         private void onRestart() {
   1168             // Only restart if halted
   1169             if (mState.current() == State.STATE_HALTED) {
   1170                 mState.set(State.STATE_RUNNING);
   1171                 getRenderTarget().focus();
   1172                 openCamera();
   1173             }
   1174         }
   1175 
   1176         private void onUpdate() {
   1177             if (mState.current() == State.STATE_RUNNING) {
   1178                 pushEvent(Event.STOP, true);
   1179                 pushEvent(Event.START, true);
   1180             }
   1181         }
   1182         private void onFrame() {
   1183             if (mState.current() == State.STATE_RUNNING) {
   1184                 updateRotation();
   1185                 mCamFrameHandler.updateServerFrame();
   1186             }
   1187         }
   1188 
   1189         private void onTearDown() {
   1190             if (mState.current() == State.STATE_STOPPED) {
   1191                 // Remove all listeners. This will release their resources
   1192                 for (CameraListener listener : mCamListeners) {
   1193                     removeListener(listener);
   1194                 }
   1195                 mCamListeners.clear();
   1196             } else {
   1197                 Log.e("CameraStreamer", "Could not tear-down CameraStreamer as camera still "
   1198                         + "seems to be running!");
   1199             }
   1200         }
   1201 
   1202         private void createCamFrameHandler() {
   1203             // TODO: For now we simply assert that OpenGL is supported. Later on, we should add
   1204             // a CamFrameHandler that does not depend on OpenGL.
   1205             getContext().assertOpenGLSupported();
   1206             if (VERSION.SDK_INT >= 16) {
   1207                 mCamFrameHandler = new CamFrameHandlerJB();
   1208             } else if (VERSION.SDK_INT >= 15) {
   1209                 mCamFrameHandler = new CamFrameHandlerICS();
   1210             } else {
   1211                 mCamFrameHandler = new CamFrameHandlerGB();
   1212             }
   1213         }
   1214 
   1215         private void updateRotation() {
   1216             if (mDisplay != null) {
   1217                 updateDisplayRotation(mDisplay.getRotation());
   1218             }
   1219         }
   1220 
   1221         private synchronized void updateDisplayRotation(int rotation) {
   1222             switch (rotation) {
   1223                 case Surface.ROTATION_0:
   1224                     onUpdateOrientation(0);
   1225                     break;
   1226                 case Surface.ROTATION_90:
   1227                     onUpdateOrientation(90);
   1228                     break;
   1229                 case Surface.ROTATION_180:
   1230                     onUpdateOrientation(180);
   1231                     break;
   1232                 case Surface.ROTATION_270:
   1233                     onUpdateOrientation(270);
   1234                     break;
   1235                 default:
   1236                     throw new IllegalArgumentException("Unsupported display rotation constant! Use "
   1237                         + "one of the Surface.ROTATION_ constants!");
   1238             }
   1239         }
   1240 
   1241         private RenderTarget getRenderTarget() {
   1242             if (mRenderTarget == null) {
   1243                 mRenderTarget = RenderTarget.newTarget(1, 1);
   1244             }
   1245             return mRenderTarget;
   1246         }
   1247 
   1248         private void updateCamera() {
   1249             synchronized (mState) {
   1250                 mCamId = getCameraId();
   1251                 updateCameraOrientation(mCamId);
   1252                 mCamera = Camera.open(mCamId);
   1253                 initCameraParameters();
   1254             }
   1255         }
   1256 
   1257         private void updateCameraOrientation(int camId) {
   1258             CameraInfo cameraInfo = new CameraInfo();
   1259             Camera.getCameraInfo(camId, cameraInfo);
   1260             mCamOrientation = cameraInfo.orientation;
   1261             mOrientation = -1;  // Forces recalculation to match display
   1262             mActualFacing = (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT)
   1263                 ? FACING_FRONT
   1264                 : FACING_BACK;
   1265         }
   1266 
   1267         private int getCameraId() {
   1268             int camCount = Camera.getNumberOfCameras();
   1269             if (camCount == 0) {
   1270                 throw new RuntimeException("Device does not have any cameras!");
   1271             } else if (mRequestedFacing == FACING_DONTCARE) {
   1272                 // Simply return first camera if mRequestedFacing is don't care
   1273                 return 0;
   1274             }
   1275 
   1276             // Attempt to find requested camera
   1277             boolean useFrontCam = (mRequestedFacing == FACING_FRONT);
   1278             CameraInfo cameraInfo = new CameraInfo();
   1279             for (int i = 0; i < camCount; ++i) {
   1280                 Camera.getCameraInfo(i, cameraInfo);
   1281                 if ((cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) == useFrontCam) {
   1282                     return i;
   1283                 }
   1284             }
   1285             throw new RuntimeException("Could not find a camera facing (" + mRequestedFacing
   1286                     + ")!");
   1287         }
   1288 
   1289         private void initCameraParameters() {
   1290             Camera.Parameters params = mCamera.getParameters();
   1291 
   1292             // Find closest preview size
   1293             mActualDims =
   1294                 findClosestPreviewSize(mRequestedPreviewWidth, mRequestedPreviewHeight, params);
   1295             mCamFrameHandler.setCameraSize(mActualDims[0], mActualDims[1]);
   1296             params.setPreviewSize(mActualDims[0], mActualDims[1]);
   1297             // Find closest picture size
   1298             int[] dims =
   1299                 findClosestPictureSize(mRequestedPictureWidth, mRequestedPictureHeight, params);
   1300             params.setPictureSize(dims[0], dims[1]);
   1301 
   1302             // Find closest FPS
   1303             int closestRange[] = findClosestFpsRange(mRequestedFramesPerSec, params);
   1304             params.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
   1305                                       closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
   1306 
   1307             // Set flash mode (if supported)
   1308             if (params.getFlashMode() != null) {
   1309                 params.setFlashMode(mFlashMode);
   1310             }
   1311 
   1312             mCamera.setParameters(params);
   1313         }
   1314 
   1315         private int[] findClosestPreviewSize(int width, int height, Camera.Parameters parameters) {
   1316             List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
   1317             return findClosestSizeFromList(width, height, previewSizes);
   1318         }
   1319 
   1320         private int[] findClosestPictureSize(int width, int height, Camera.Parameters parameters) {
   1321             List<Camera.Size> pictureSizes = parameters.getSupportedPictureSizes();
   1322             return findClosestSizeFromList(width, height, pictureSizes);
   1323         }
   1324 
   1325         private int[] findClosestSizeFromList(int width, int height, List<Camera.Size> sizes) {
   1326             int closestWidth = -1;
   1327             int closestHeight = -1;
   1328             int smallestWidth = sizes.get(0).width;
   1329             int smallestHeight =  sizes.get(0).height;
   1330             for (Camera.Size size : sizes) {
   1331                 // Best match defined as not being larger in either dimension than
   1332                 // the requested size, but as close as possible. The below isn't a
   1333                 // stable selection (reording the size list can give different
   1334                 // results), but since this is a fallback nicety, that's acceptable.
   1335                 if ( size.width <= width &&
   1336                      size.height <= height &&
   1337                      size.width >= closestWidth &&
   1338                      size.height >= closestHeight) {
   1339                     closestWidth = size.width;
   1340                     closestHeight = size.height;
   1341                 }
   1342                 if ( size.width < smallestWidth &&
   1343                      size.height < smallestHeight) {
   1344                     smallestWidth = size.width;
   1345                     smallestHeight = size.height;
   1346                 }
   1347             }
   1348             if (closestWidth == -1) {
   1349                 // Requested size is smaller than any listed size; match with smallest possible
   1350                 closestWidth = smallestWidth;
   1351                 closestHeight = smallestHeight;
   1352             }
   1353             int[] closestSize = {closestWidth, closestHeight};
   1354             return closestSize;
   1355         }
   1356 
   1357         private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
   1358             List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
   1359             int[] closestRange = supportedFpsRanges.get(0);
   1360             int fpsk = fps * 1000;
   1361             int minDiff = 1000000;
   1362             for (int[] range : supportedFpsRanges) {
   1363                 int low = range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
   1364                 int high = range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
   1365                 if (low <= fpsk && high >= fpsk) {
   1366                     int diff = (fpsk - low) + (high - fpsk);
   1367                     if (diff < minDiff) {
   1368                         closestRange = range;
   1369                         minDiff = diff;
   1370                     }
   1371                 }
   1372             }
   1373             mActualFramesPerSec = closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000;
   1374             return closestRange;
   1375         }
   1376 
   1377         private void onUpdateOrientation(int orientation) {
   1378             // First we calculate the camera rotation.
   1379             int rotation = (mActualFacing == FACING_FRONT)
   1380                     ? (mCamOrientation + orientation) % 360
   1381                     : (mCamOrientation - orientation + 360) % 360;
   1382             if (rotation != mCamRotation) {
   1383                 synchronized (this) {
   1384                     mCamRotation = rotation;
   1385                 }
   1386             }
   1387 
   1388             // We compensate for mirroring in the orientation. This differs from the rotation,
   1389             // where we are invariant to mirroring.
   1390             int fixedOrientation = rotation;
   1391             if (mActualFacing == FACING_FRONT && mCamFrameHandler.isFrontMirrored()) {
   1392                 fixedOrientation = (360 - rotation) % 360;  // compensate the mirror
   1393             }
   1394             if (mOrientation != fixedOrientation) {
   1395                 mOrientation = fixedOrientation;
   1396                 mCamFrameHandler.onUpdateCameraOrientation(mOrientation);
   1397             }
   1398         }
   1399 
   1400         private void openCamera() {
   1401             // Acquire lock for camera
   1402             try {
   1403                 if (!mCameraLock.tryLock(MAX_CAMERA_WAIT_TIME, TimeUnit.SECONDS)) {
   1404                     throw new RuntimeException("Timed out while waiting to acquire camera!");
   1405                 }
   1406             } catch (InterruptedException e) {
   1407                 throw new RuntimeException("Interrupted while waiting to acquire camera!");
   1408             }
   1409 
   1410             // Make sure external entities are not holding camera. We need to hold the lock until
   1411             // the preview is started again.
   1412             Object lockContext = new Object();
   1413             mExternalCameraLock.lock(lockContext);
   1414 
   1415             // Need to synchronize this as many of the member values are modified during setup.
   1416             synchronized (this) {
   1417                 updateCamera();
   1418                 updateRotation();
   1419                 mCamFrameHandler.setupServerFrame();
   1420             }
   1421 
   1422             mCamera.startPreview();
   1423 
   1424             // Inform listeners
   1425             synchronized (mCamListeners) {
   1426                 for (CameraListener listener : mCamListeners) {
   1427                     listener.onCameraOpened(CameraStreamer.this);
   1428                 }
   1429             }
   1430             mExternalCameraLock.unlock(lockContext);
   1431             // New camera started
   1432             mCameraReadyLock.lock();
   1433             mCameraReady.signal();
   1434             mCameraReadyLock.unlock();
   1435         }
   1436 
   1437         /**
   1438          * Creates an instance of MediaRecorder to be used for the streamer.
   1439          * User should call the functions in the following sequence:<p>
   1440          *   {@link #createRecorder}<p>
   1441          *   {@link #startRecording}<p>
   1442          *   {@link #stopRecording}<p>
   1443          *   {@link #releaseRecorder}<p>
   1444          * @param outputPath the output video path for the recorder
   1445          * @param profile the recording {@link CamcorderProfile} which has parameters indicating
   1446          *  the resolution, quality etc.
   1447          */
   1448         public void createRecorder(String outputPath, CamcorderProfile profile) {
   1449             lockCamera(this);
   1450             mCamera.unlock();
   1451             if (mRecorder != null) {
   1452                 mRecorder.release();
   1453             }
   1454             mRecorder = new MediaRecorder();
   1455             mRecorder.setCamera(mCamera);
   1456             mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
   1457             mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
   1458             mRecorder.setProfile(profile);
   1459             mRecorder.setOutputFile(outputPath);
   1460             try {
   1461                 mRecorder.prepare();
   1462             } catch (Exception e) {
   1463                 throw new RuntimeException(e);
   1464             }
   1465         }
   1466 
   1467         /**
   1468          * Starts recording video using the created MediaRecorder object
   1469          */
   1470         public void startRecording() {
   1471             if (mRecorder == null) {
   1472                 throw new RuntimeException("No recorder created");
   1473             }
   1474             mRecorder.start();
   1475         }
   1476 
   1477         /**
   1478          * Stops recording video
   1479          */
   1480         public void stopRecording() {
   1481             if (mRecorder == null) {
   1482                 throw new RuntimeException("No recorder created");
   1483             }
   1484             mRecorder.stop();
   1485         }
   1486 
   1487         /**
   1488          * Release the resources held by the MediaRecorder, call this after done recording.
   1489          */
   1490         public void releaseRecorder() {
   1491             if (mRecorder == null) {
   1492                 throw new RuntimeException("No recorder created");
   1493             }
   1494             mRecorder.release();
   1495             mRecorder = null;
   1496             mCamera.lock();
   1497             unlockCamera(this);
   1498         }
   1499 
   1500         private void closeCamera() {
   1501             Object lockContext = new Object();
   1502             mExternalCameraLock.lock(lockContext);
   1503             if (mCamera != null) {
   1504                 mCamera.stopPreview();
   1505                 mCamera.release();
   1506                 mCamera = null;
   1507             }
   1508             mCameraLock.unlock();
   1509             mCamFrameHandler.release();
   1510             mExternalCameraLock.unlock(lockContext);
   1511             // Inform listeners
   1512             synchronized (mCamListeners) {
   1513                 for (CameraListener listener : mCamListeners) {
   1514                     listener.onCameraClosed(CameraStreamer.this);
   1515                 }
   1516             }
   1517         }
   1518 
   1519     }
   1520 
   1521     /**
   1522      * The frame-client callback interface.
   1523      * FrameClients, that wish to receive Frames from the camera must implement this callback
   1524      * method.
   1525      * Note, that this method is called on the Camera server thread. However, the
   1526      * {@code getLatestFrame()} method must be called from the client thread.
   1527      */
   1528     public static interface FrameClient {
   1529         public void onCameraFrameAvailable();
   1530     }
   1531 
   1532     /**
   1533      * The CameraListener callback interface.
   1534      * This interface allows observers to monitor the CameraStreamer and respond to stream open
   1535      * and close events.
   1536      */
   1537     public static interface CameraListener {
   1538         /**
   1539          * Called when the camera is opened and begins producing frames.
   1540          * This is also called when settings have changed that caused the camera to be reopened.
   1541          */
   1542         public void onCameraOpened(CameraStreamer camera);
   1543 
   1544         /**
   1545          * Called when the camera is closed and stops producing frames.
   1546          */
   1547         public void onCameraClosed(CameraStreamer camera);
   1548     }
   1549 
   1550     /**
   1551      * Manually update the display rotation.
   1552      * You do not need to call this, if the camera is bound to a display, or your app does not
   1553      * support multiple orientations.
   1554      */
   1555     public void updateDisplayRotation(int rotation) {
   1556         mCameraRunner.updateDisplayRotation(rotation);
   1557     }
   1558 
   1559     /**
   1560      * Bind the camera to your Activity's display.
   1561      * Use this, if your Activity supports multiple display orientation, and you would like the
   1562      * camera to update accordingly when the orientation is changed.
   1563      */
   1564     public void bindToDisplay(Display display) {
   1565         mCameraRunner.bindToDisplay(display);
   1566     }
   1567 
   1568     /**
   1569      * Sets the desired preview size.
   1570      * Note that the actual width and height may vary.
   1571      *
   1572      * @param width The desired width of the preview camera stream.
   1573      * @param height The desired height of the preview camera stream.
   1574      */
   1575     public void setDesiredPreviewSize(int width, int height) {
   1576         mCameraRunner.setDesiredPreviewSize(width, height);
   1577     }
   1578 
   1579     /**
   1580      * Sets the desired picture size.
   1581      * Note that the actual width and height may vary.
   1582      *
   1583      * @param width The desired picture width.
   1584      * @param height The desired picture height.
   1585      */
   1586     public void setDesiredPictureSize(int width, int height) {
   1587         mCameraRunner.setDesiredPictureSize(width, height);
   1588     }
   1589 
   1590     /**
   1591      * Sets the desired camera frame-rate.
   1592      * Note, that the actual frame-rate may vary.
   1593      *
   1594      * @param fps The desired FPS.
   1595      */
   1596     public void setDesiredFrameRate(int fps) {
   1597         mCameraRunner.setDesiredFrameRate(fps);
   1598     }
   1599 
   1600     /**
   1601      * Sets the camera facing direction.
   1602      *
   1603      * Specify {@code FACING_DONTCARE} (default) if you would like the CameraStreamer to choose
   1604      * the direction. When specifying any other direction be sure to first check whether the
   1605      * device supports the desired facing.
   1606      *
   1607      * @param facing The desired camera facing direction.
   1608      */
   1609     public void setFacing(int facing) {
   1610         mCameraRunner.setFacing(facing);
   1611     }
   1612 
   1613     /**
   1614      * Set whether to flip the camera image horizontally when using the front facing camera.
   1615      */
   1616     public void setFlipFrontCamera(boolean flipFront) {
   1617         mCameraRunner.setFlipFrontCamera(flipFront);
   1618     }
   1619 
   1620     /**
   1621      * Sets the camera flash mode.
   1622      *
   1623      * This must be one of the String constants defined in the Camera.Parameters class.
   1624      *
   1625      * @param flashMode A String constant specifying the flash mode.
   1626      */
   1627     public void setFlashMode(String flashMode) {
   1628         mCameraRunner.setFlashMode(flashMode);
   1629     }
   1630 
   1631     /**
   1632      * Returns the current flash mode.
   1633      *
   1634      * This returns the currently running camera's flash-mode, or NULL if flash modes are not
   1635      * supported on that camera.
   1636      *
   1637      * @return The flash mode String, or NULL if flash modes are not supported.
   1638      */
   1639     public String getFlashMode() {
   1640         return mCameraRunner.getFlashMode();
   1641     }
   1642 
   1643     /**
   1644      * Get the actual camera facing.
   1645      * Returns 0 if actual facing is not yet known.
   1646      */
   1647     public int getCameraFacing() {
   1648         return mCameraRunner.getCameraFacing();
   1649     }
   1650 
   1651     /**
   1652      * Get the current camera rotation.
   1653      *
   1654      * Use this rotation if you want to snap pictures from the camera and need to rotate the
   1655      * picture to be up-right.
   1656      *
   1657      * @return the current camera rotation.
   1658      */
   1659     public int getCameraRotation() {
   1660         return mCameraRunner.getCameraRotation();
   1661     }
   1662 
   1663     /**
   1664      * Specifies whether or not the camera supports hardware face detection.
   1665      * @return true, if the camera supports hardware face detection.
   1666      */
   1667     public boolean supportsHardwareFaceDetection() {
   1668         return mCameraRunner.supportsHardwareFaceDetection();
   1669     }
   1670 
   1671     /**
   1672      * Returns the camera facing that is chosen when DONT_CARE is specified.
   1673      * Returns 0 if neither a front nor back camera could be found.
   1674      */
   1675     public static int getDefaultFacing() {
   1676         int camCount = Camera.getNumberOfCameras();
   1677         if (camCount == 0) {
   1678             return 0;
   1679         } else {
   1680             CameraInfo cameraInfo = new CameraInfo();
   1681             Camera.getCameraInfo(0, cameraInfo);
   1682             return (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT)
   1683                 ? FACING_FRONT
   1684                 : FACING_BACK;
   1685         }
   1686     }
   1687 
   1688     /**
   1689      * Get the actual camera width.
   1690      * Returns 0 if actual width is not yet known.
   1691      */
   1692     public int getCameraWidth() {
   1693         return mCameraRunner.getCameraWidth();
   1694     }
   1695 
   1696     /**
   1697      * Get the actual camera height.
   1698      * Returns 0 if actual height is not yet known.
   1699      */
   1700     public int getCameraHeight() {
   1701         return mCameraRunner.getCameraHeight();
   1702     }
   1703 
   1704     /**
   1705      * Get the actual camera frame-rate.
   1706      * Returns 0 if actual frame-rate is not yet known.
   1707      */
   1708     public int getCameraFrameRate() {
   1709         return mCameraRunner.getCameraFrameRate();
   1710     }
   1711 
   1712     /**
   1713      * Returns true if the camera can be started at this point.
   1714      */
   1715     public boolean canStart() {
   1716         return mCameraRunner.canStart();
   1717     }
   1718 
   1719     /**
   1720      * Returns true if the camera is currently running.
   1721      */
   1722     public boolean isRunning() {
   1723         return mCameraRunner.isRunning();
   1724     }
   1725 
   1726     /**
   1727      * Starts the camera.
   1728      */
   1729     public void start() {
   1730         mCameraRunner.pushEvent(Event.START, true);
   1731     }
   1732 
   1733     /**
   1734      * Stops the camera.
   1735      */
   1736     public void stop() {
   1737         mCameraRunner.pushEvent(Event.STOP, true);
   1738     }
   1739 
   1740     /**
   1741      * Stops the camera and waits until it is completely closed. Generally, this should not be
   1742      * called in the UI thread, but may be necessary if you need the camera to be closed before
   1743      * performing subsequent steps.
   1744      */
   1745     public void stopAndWait() {
   1746         mCameraRunner.pushEvent(Event.STOP, true);
   1747         try {
   1748             if (!mCameraLock.tryLock(MAX_CAMERA_WAIT_TIME, TimeUnit.SECONDS)) {
   1749                 Log.w("CameraStreamer", "Time-out waiting for camera to close!");
   1750             }
   1751         } catch (InterruptedException e) {
   1752             Log.w("CameraStreamer", "Interrupted while waiting for camera to close!");
   1753         }
   1754         mCameraLock.unlock();
   1755     }
   1756 
   1757     /**
   1758      * Registers a listener to handle camera state changes.
   1759      */
   1760     public void addListener(CameraListener listener) {
   1761         mCameraRunner.addListener(listener);
   1762     }
   1763 
   1764     /**
   1765      * Unregisters a listener to handle camera state changes.
   1766      */
   1767     public void removeListener(CameraListener listener) {
   1768         mCameraRunner.removeListener(listener);
   1769     }
   1770 
   1771     /**
   1772      * Registers the frame-client with the camera.
   1773      * This MUST be called from the client thread!
   1774      */
   1775     public void registerClient(FrameClient client) {
   1776         mCameraRunner.getCamFrameHandler().registerClient(client);
   1777     }
   1778 
   1779     /**
   1780      * Unregisters the frame-client with the camera.
   1781      * This MUST be called from the client thread!
   1782      */
   1783     public void unregisterClient(FrameClient client) {
   1784         mCameraRunner.getCamFrameHandler().unregisterClient(client);
   1785     }
   1786 
   1787     /**
   1788      * Gets the latest camera frame for the client.
   1789      *
   1790      * This must be called from the same thread as the {@link #registerClient(FrameClient)} call!
   1791      * The frame passed in will be resized by the camera streamer to fit the camera frame.
   1792      * Returns false if the frame could not be grabbed. This may happen if the camera has been
   1793      * closed in the meantime, and its resources let go.
   1794      *
   1795      * @return true, if the frame was grabbed successfully.
   1796      */
   1797     public boolean getLatestFrame(FrameImage2D targetFrame) {
   1798         return mCameraRunner.grabFrame(targetFrame);
   1799     }
   1800 
   1801     /**
   1802      * Expose the underlying android.hardware.Camera object.
   1803      * Use the returned object with care: some camera functions may break the functionality
   1804      * of CameraStreamer.
   1805      * @return the Camera object.
   1806      */
   1807     @Deprecated
   1808     public Camera getCamera() {
   1809         return mCameraRunner.getCamera();
   1810     }
   1811 
   1812     /**
   1813      * Obtain access to the underlying android.hardware.Camera object.
   1814      * This grants temporary access to the internal Camera handle. Once you are done using the
   1815      * handle you must call {@link #unlockCamera(Object)}. While you are holding the Camera,
   1816      * it will not be modified or released by the CameraStreamer. The Camera object return is
   1817      * guaranteed to have the preview running.
   1818      *
   1819      * The CameraStreamer does not account for changes you make to the Camera. That is, if you
   1820      * change the Camera unexpectedly this may cause unintended behavior by the streamer.
   1821      *
   1822      * Note that the returned object may be null. This can happen when the CameraStreamer is not
   1823      * running, or is just transitioning to another Camera, such as during a switch from front to
   1824      * back Camera.
   1825      * @param context an object used as a context for locking and unlocking. lockCamera and
   1826      *   unlockCamera should use the same context object.
   1827      * @return The Camera object.
   1828      */
   1829     public Camera lockCamera(Object context) {
   1830         return mCameraRunner.lockCamera(context);
   1831     }
   1832 
   1833     /**
   1834      * Release the acquire Camera object.
   1835      * @param context the context object that used when lockCamera is called.
   1836      */
   1837     public void unlockCamera(Object context) {
   1838         mCameraRunner.unlockCamera(context);
   1839     }
   1840 
   1841     /**
   1842      * Creates an instance of MediaRecorder to be used for the streamer.
   1843      * User should call the functions in the following sequence:<p>
   1844      *   {@link #createRecorder}<p>
   1845      *   {@link #startRecording}<p>
   1846      *   {@link #stopRecording}<p>
   1847      *   {@link #releaseRecorder}<p>
   1848      * @param path the output video path for the recorder
   1849      * @param profile the recording {@link CamcorderProfile} which has parameters indicating
   1850      *  the resolution, quality etc.
   1851      */
   1852     public void createRecorder(String path, CamcorderProfile profile) {
   1853         mCameraRunner.createRecorder(path, profile);
   1854     }
   1855 
   1856     public void releaseRecorder() {
   1857         mCameraRunner.releaseRecorder();
   1858     }
   1859 
   1860     public void startRecording() {
   1861         mCameraRunner.startRecording();
   1862     }
   1863 
   1864     public void stopRecording() {
   1865         mCameraRunner.stopRecording();
   1866     }
   1867 
   1868     /**
   1869      * Retrieve the ID of the currently used camera.
   1870      * @return the ID of the currently used camera.
   1871      */
   1872     public int getCameraId() {
   1873         return mCameraRunner.getCurrentCameraId();
   1874     }
   1875 
   1876     /**
   1877      * @return The number of cameras available for streaming on this device.
   1878      */
   1879     public static int getNumberOfCameras() {
   1880         // Currently, this is just the number of cameras that are available on the device.
   1881         return Camera.getNumberOfCameras();
   1882     }
   1883 
   1884     CameraStreamer(MffContext context) {
   1885         mCameraRunner = new CameraRunnable(context);
   1886     }
   1887 
   1888     /** Halt is like stop, but may be resumed using restart(). */
   1889     void halt() {
   1890         mCameraRunner.pushEvent(Event.HALT, true);
   1891     }
   1892 
   1893     /** Restart starts the camera only if previously halted. */
   1894     void restart() {
   1895         mCameraRunner.pushEvent(Event.RESTART, true);
   1896     }
   1897 
   1898     static boolean requireDummySurfaceView() {
   1899         return VERSION.SDK_INT < 15;
   1900     }
   1901 
   1902     void tearDown() {
   1903         mCameraRunner.pushEvent(Event.TEARDOWN, true);
   1904     }
   1905 }
   1906 
   1907