/ocr/ocrservice/src/com/googlecode/eyesfree/opticflow/FrameLooper.java
Java | 294 lines | 170 code | 61 blank | 63 comment | 20 complexity | 508ecf25c2171b547bd4f16435702a66 MD5 | raw file
1/* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.googlecode.eyesfree.opticflow; 18 19import android.graphics.Canvas; 20import android.graphics.Color; 21import android.graphics.Paint; 22import android.graphics.Rect; 23import android.util.Log; 24 25import com.googlecode.eyesfree.env.Size; 26import com.googlecode.eyesfree.env.Stopwatch; 27import com.googlecode.eyesfree.opticflow.FrameProducer.Frame; 28import com.googlecode.eyesfree.opticflow.FrameProducer.FrameReceiver; 29 30import java.util.ArrayList; 31import java.util.Vector; 32 33/** 34 * Handles looping of the preview frames. FrameProcessors may be registered, and 35 * each will get access to the TimestampedFrame buffer wrapper in sequence. 36 * FrameProcessors may access data that came before them, so care must be taken 37 * that they are added in the right order. Modified by Alan Viverette from 38 * Andrew Harp's original source. 39 * 40 * @author Andrew Harp 41 * @author alanv@google.com (Alan Viverette) 42 */ 43public class FrameLooper implements FrameReceiver { 44 private static final String TAG = "FrameLooper"; 45 46 private final FrameProducer frameProducer; 47 48 private final DebugView debugView; 49 50 private final Stopwatch stopwatch; 51 52 // The thread that does the actual frame processing. Also acts as a flag for 53 // whether the processing loop is active or not. 54 private final ProcessingThread processingThreads[]; 55 56 // The number of preview frames that have been received from the camera. 57 private int numPreviewFrames; 58 59 private boolean firstRun; 60 61 private boolean running; 62 63 private Size size; 64 65 ArrayList<FrameProcessor> allPreviewProcessors; 66 67 public FrameLooper( 68 final FrameProducer cameraManager, final DebugView debugView, final int[] delays) { 69 this.frameProducer = cameraManager; 70 this.debugView = debugView; 71 72 this.stopwatch = new Stopwatch(); 73 74 this.firstRun = true; 75 this.running = false; 76 77 int width = frameProducer.getFrameWidth(); 78 int height = frameProducer.getFrameHeight(); 79 size = new Size(width, height); 80 81 this.processingThreads = new ProcessingThread[delays.length]; 82 83 for (int level = 0; level < delays.length; ++level) { 84 // The last processing thread should have priority 85 // Thread.MIN_PRIORITY, every thread before it should have 86 // incrementally more priority. 87 final int priority = Thread.MIN_PRIORITY + delays.length - (level + 1); 88 processingThreads[level] = new ProcessingThread(this, level, delays[level], priority); 89 } 90 } 91 92 public void addPreviewProcessor(final FrameProcessor handler, final int level) { 93 synchronized (processingThreads[level]) { 94 processingThreads[level].addProcessor(handler); 95 allPreviewProcessors = null; 96 } 97 } 98 99 /** 100 * @return A copy of the current processor list that is safe to modify. 101 */ 102 public ArrayList<FrameProcessor> getAllProcessors() { 103 if (allPreviewProcessors == null) { 104 allPreviewProcessors = new ArrayList<FrameProcessor>(); 105 106 for (final ProcessingThread thread : processingThreads) { 107 allPreviewProcessors.addAll(thread.getProcessors()); 108 } 109 } 110 return allPreviewProcessors; 111 } 112 113 final ArrayList<ProcessingThread> readyThreads = new ArrayList<ProcessingThread>(); 114 115 @Override 116 public synchronized void onFrameReceived(final Frame frame) { 117 ++numPreviewFrames; 118 119 // Create a new TimestampedFrame to wrap the raw byte[]. 120 final TimestampedFrame previewFrame = new TimestampedFrame(frame); 121 122 // TODO(alanv): Why does this run on the main thread?! 123 processingThreads[0].preprocess(previewFrame); 124 125 // readyThreads is a list of all threads that are done processing the 126 // last 127 // iteration. 128 readyThreads.clear(); 129 for (int i = 1; i < processingThreads.length; ++i) { 130 final ProcessingThread thread = processingThreads[i]; 131 if (thread.isReady()) { 132 readyThreads.add(thread); 133 thread.preprocess(previewFrame); 134 } 135 } 136 137 // This is synchronized so that stopLoop is guaranteed to be called (if 138 // desired) before preview frame processing finishes. 139 processingThreads[0].processFrame(previewFrame); 140 if (running) { 141 frameProducer.requestFrame(this); 142 } 143 144 if (DebugView.isVisible) { 145 debugView.invalidate(); 146 } 147 148 for (final ProcessingThread thread : readyThreads) { 149 thread.sendFrame(previewFrame); 150 } 151 } 152 153 public synchronized void doneProcessing(final TimestampedFrame frame) { 154 if (frame.allThreadsDone()) { 155 frame.clearRawData(); 156 } 157 } 158 159 /** 160 * Starts the loop for a finite number of frames. 161 */ 162 public synchronized void startLoop() { 163 stopwatch.start(); 164 running = true; 165 166 numPreviewFrames = 0; 167 168 Log.d(TAG, "Starting frame loop."); 169 170 if (firstRun) { 171 for (int i = 1; i < processingThreads.length; ++i) { 172 processingThreads[i].start(); 173 } 174 } 175 firstRun = false; 176 177 startAllProcessors(); 178 179 frameProducer.requestFrame(this); 180 } 181 182 /** 183 * Stops the loop at its earliest possible convenience. 184 */ 185 public synchronized void stopLoop() { 186 if (running == false) { 187 Log.w(TAG, "Stopping a FrameLooper that was already stoppped."); 188 return; 189 } 190 191 // Reset the state of preview requests. 192 frameProducer.requestFrame(null); 193 194 stopAllProcessors(); 195 running = false; 196 } 197 198 /** 199 * Guaranteed to be called at least once before the processors processes 200 * anything and every time the preview size changes. 201 */ 202 public void initAllProcessors() { 203 int width = frameProducer.getFrameWidth(); 204 int height = frameProducer.getFrameHeight(); 205 size = new Size(width, height); 206 207 for (final FrameProcessor processor : getAllProcessors()) { 208 processor.init(size); 209 } 210 } 211 212 private void startAllProcessors() { 213 for (final FrameProcessor processor : getAllProcessors()) { 214 processor.start(); 215 } 216 } 217 218 // TODO(xiaotao, andrewharp): Make sure that no frame slips through for 219 // processing after onStop() is called on processor not running in the UI 220 // thread 221 private void stopAllProcessors() { 222 for (final FrameProcessor processor : getAllProcessors()) { 223 processor.stop(); 224 } 225 } 226 227 public void drawDebug(final Canvas canvas) { 228 final ArrayList<FrameProcessor> processors = getAllProcessors(); 229 230 for (final FrameProcessor processor : processors) { 231 synchronized (processor) { 232 processor.drawDebug(canvas); 233 } 234 } 235 236 // Draw the debug text in the bottom left. 237 final Paint p = new Paint(); 238 239 final int xPos = 0; 240 int startingYPos = canvas.getHeight(); 241 242 final int kLargeTextSize = 20; 243 final int kSmallTextSize = 16; 244 final int kTextBufferSize = 4; 245 246 for (final FrameProcessor processor : processors) { 247 Vector<String> lines; 248 synchronized (processor) { 249 lines = processor.getDebugText(); 250 } 251 252 // TODO(andrewharp): Don't hardcode this, figure out the text 253 // length. 254 final int shadedWidth = 200; 255 256 // Each block has one large header line followed by a buffer, then N 257 // smaller lines each followed by a buffer, and then an additional 258 // buffer. 259 final int shadedHeight = kLargeTextSize + kTextBufferSize 260 + (kSmallTextSize + kTextBufferSize) * lines.size() + kTextBufferSize; 261 262 startingYPos -= shadedHeight; 263 264 p.setColor(Color.BLACK); 265 p.setAlpha(100); 266 267 int yPos = startingYPos; 268 canvas.drawRect(new Rect(xPos, yPos, xPos + shadedWidth, yPos + shadedHeight), p); 269 270 // Header line. 271 p.setAlpha(255); 272 273 p.setAntiAlias(true); 274 p.setColor(Color.CYAN); 275 p.setTextSize(kLargeTextSize); 276 yPos += kLargeTextSize + kTextBufferSize; 277 canvas.drawText(processor.getName(), xPos, yPos, p); 278 279 // Debug lines. 280 p.setColor(Color.WHITE); 281 p.setTextSize(kSmallTextSize); 282 for (final String line : lines) { 283 yPos += kSmallTextSize + kTextBufferSize; 284 canvas.drawText(line, xPos, yPos, p); 285 } 286 287 yPos += kTextBufferSize; 288 } 289 } 290 291 public boolean isRunning() { 292 return running; 293 } 294}