PageRenderTime 38ms CodeModel.GetById 16ms app.highlight 18ms RepoModel.GetById 2ms app.codeStats 0ms

/ocr/ocrservice/src/com/googlecode/eyesfree/opticflow/FrameLooper.java

http://eyes-free.googlecode.com/
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}