1 package org.tensorflow.demo; 2 3 /* 4 * Copyright 2017 The TensorFlow Authors. All Rights Reserved. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 import android.app.Fragment; 20 import android.graphics.SurfaceTexture; 21 import android.hardware.Camera; 22 import android.hardware.Camera.CameraInfo; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.util.Size; 27 import android.util.SparseIntArray; 28 import android.view.LayoutInflater; 29 import android.view.Surface; 30 import android.view.TextureView; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import java.io.IOException; 34 import java.util.List; 35 import org.tensorflow.demo.env.ImageUtils; 36 import org.tensorflow.demo.env.Logger; 37 import org.tensorflow.demo.R; // Explicit import needed for internal Google builds. 38 39 public class LegacyCameraConnectionFragment extends Fragment { 40 private Camera camera; 41 private static final Logger LOGGER = new Logger(); 42 private Camera.PreviewCallback imageListener; 43 private Size desiredSize; 44 45 /** 46 * The layout identifier to inflate for this Fragment. 47 */ 48 private int layout; 49 LegacyCameraConnectionFragment( final Camera.PreviewCallback imageListener, final int layout, final Size desiredSize)50 public LegacyCameraConnectionFragment( 51 final Camera.PreviewCallback imageListener, final int layout, final Size desiredSize) { 52 this.imageListener = imageListener; 53 this.layout = layout; 54 this.desiredSize = desiredSize; 55 } 56 57 /** 58 * Conversion from screen rotation to JPEG orientation. 59 */ 60 private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 61 62 static { ORIENTATIONS.append(Surface.ROTATION_0, 90)63 ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0)64 ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270)65 ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180)66 ORIENTATIONS.append(Surface.ROTATION_270, 180); 67 } 68 69 /** 70 * {@link android.view.TextureView.SurfaceTextureListener} handles several lifecycle events on a 71 * {@link TextureView}. 72 */ 73 private final TextureView.SurfaceTextureListener surfaceTextureListener = 74 new TextureView.SurfaceTextureListener() { 75 @Override 76 public void onSurfaceTextureAvailable( 77 final SurfaceTexture texture, final int width, final int height) { 78 79 int index = getCameraId(); 80 camera = Camera.open(index); 81 82 try { 83 Camera.Parameters parameters = camera.getParameters(); 84 List<String> focusModes = parameters.getSupportedFocusModes(); 85 if (focusModes != null 86 && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { 87 parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); 88 } 89 List<Camera.Size> cameraSizes = parameters.getSupportedPreviewSizes(); 90 Size[] sizes = new Size[cameraSizes.size()]; 91 int i = 0; 92 for (Camera.Size size : cameraSizes) { 93 sizes[i++] = new Size(size.width, size.height); 94 } 95 Size previewSize = 96 CameraConnectionFragment.chooseOptimalSize( 97 sizes, desiredSize.getWidth(), desiredSize.getHeight()); 98 parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight()); 99 camera.setDisplayOrientation(90); 100 camera.setParameters(parameters); 101 camera.setPreviewTexture(texture); 102 } catch (IOException exception) { 103 camera.release(); 104 } 105 106 camera.setPreviewCallbackWithBuffer(imageListener); 107 Camera.Size s = camera.getParameters().getPreviewSize(); 108 camera.addCallbackBuffer(new byte[ImageUtils.getYUVByteSize(s.height, s.width)]); 109 110 textureView.setAspectRatio(s.height, s.width); 111 112 camera.startPreview(); 113 } 114 115 @Override 116 public void onSurfaceTextureSizeChanged( 117 final SurfaceTexture texture, final int width, final int height) {} 118 119 @Override 120 public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) { 121 return true; 122 } 123 124 @Override 125 public void onSurfaceTextureUpdated(final SurfaceTexture texture) {} 126 }; 127 128 /** 129 * An {@link AutoFitTextureView} for camera preview. 130 */ 131 private AutoFitTextureView textureView; 132 133 /** 134 * An additional thread for running tasks that shouldn't block the UI. 135 */ 136 private HandlerThread backgroundThread; 137 138 @Override onCreateView( final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState)139 public View onCreateView( 140 final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { 141 return inflater.inflate(layout, container, false); 142 } 143 144 @Override onViewCreated(final View view, final Bundle savedInstanceState)145 public void onViewCreated(final View view, final Bundle savedInstanceState) { 146 textureView = (AutoFitTextureView) view.findViewById(R.id.texture); 147 } 148 149 @Override onActivityCreated(final Bundle savedInstanceState)150 public void onActivityCreated(final Bundle savedInstanceState) { 151 super.onActivityCreated(savedInstanceState); 152 } 153 154 @Override onResume()155 public void onResume() { 156 super.onResume(); 157 startBackgroundThread(); 158 // When the screen is turned off and turned back on, the SurfaceTexture is already 159 // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open 160 // a camera and start preview from here (otherwise, we wait until the surface is ready in 161 // the SurfaceTextureListener). 162 163 if (textureView.isAvailable()) { 164 camera.startPreview(); 165 } else { 166 textureView.setSurfaceTextureListener(surfaceTextureListener); 167 } 168 } 169 170 @Override onPause()171 public void onPause() { 172 stopCamera(); 173 stopBackgroundThread(); 174 super.onPause(); 175 } 176 177 /** 178 * Starts a background thread and its {@link Handler}. 179 */ startBackgroundThread()180 private void startBackgroundThread() { 181 backgroundThread = new HandlerThread("CameraBackground"); 182 backgroundThread.start(); 183 } 184 185 /** 186 * Stops the background thread and its {@link Handler}. 187 */ stopBackgroundThread()188 private void stopBackgroundThread() { 189 backgroundThread.quitSafely(); 190 try { 191 backgroundThread.join(); 192 backgroundThread = null; 193 } catch (final InterruptedException e) { 194 LOGGER.e(e, "Exception!"); 195 } 196 } 197 stopCamera()198 protected void stopCamera() { 199 if (camera != null) { 200 camera.stopPreview(); 201 camera.setPreviewCallback(null); 202 camera.release(); 203 camera = null; 204 } 205 } 206 getCameraId()207 private int getCameraId() { 208 CameraInfo ci = new CameraInfo(); 209 for (int i = 0; i < Camera.getNumberOfCameras(); i++) { 210 Camera.getCameraInfo(i, ci); 211 if (ci.facing == CameraInfo.CAMERA_FACING_BACK) 212 return i; 213 } 214 return -1; // No camera found 215 } 216 } 217