blob: ed9dde1e01584d705e433ff92e0a6d722dace4c7 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.camera;
import android.util.Log;
/**
* Class to handle the processing of each frame by Mosaicer.
*/
public class MosaicFrameProcessor {
private static final String TAG = "MosaicFrameProcessor";
private static final int NUM_FRAMES_IN_BUFFER = 2;
private static final int MAX_NUMBER_OF_FRAMES = 100;
private static final int MOSAIC_RET_CODE_INDEX = 10;
private static final int FRAME_COUNT_INDEX = 9;
private static final int X_COORD_INDEX = 2;
private static final int Y_COORD_INDEX = 5;
private static final int HR_TO_LR_DOWNSAMPLE_FACTOR = 4;
private static final int WINDOW_SIZE = 3;
private Mosaic mMosaicer;
private boolean mIsMosaicMemoryAllocated = false;
private final long [] mFrameTimestamp = new long[NUM_FRAMES_IN_BUFFER];
private float mTranslationLastX;
private float mTranslationLastY;
private int mFillIn = 0;
private int mTotalFrameCount = 0;
private long mLastProcessedFrameTimestamp = 0;
private int mLastProcessFrameIdx = -1;
private int mCurrProcessFrameIdx = -1;
// Panning rate is in unit of percentage of image content translation / second.
// Use the moving average to calculate the panning rate.
private float mPanningRateX;
private float mPanningRateY;
private float[] mDeltaX = new float[WINDOW_SIZE];
private float[] mDeltaY = new float[WINDOW_SIZE];
private float[] mDeltaTime = new float[WINDOW_SIZE];
private int mOldestIdx = 0;
private float mTotalTranslationX = 0f;
private float mTotalTranslationY = 0f;
private float mTotalDeltaTime = 0f;
private ProgressListener mProgressListener;
private int mPreviewWidth;
private int mPreviewHeight;
private int mPreviewBufferSize;
private static MosaicFrameProcessor sMosaicFrameProcessor; // singleton
public interface ProgressListener {
public void onProgress(boolean isFinished, float panningRateX, float panningRateY,
float progressX, float progressY);
}
public static MosaicFrameProcessor getInstance() {
if (sMosaicFrameProcessor == null) {
sMosaicFrameProcessor = new MosaicFrameProcessor();
}
return sMosaicFrameProcessor;
}
private MosaicFrameProcessor() {
mMosaicer = new Mosaic();
}
public void setProgressListener(ProgressListener listener) {
mProgressListener = listener;
}
public int reportProgress(boolean hires, boolean cancel) {
return mMosaicer.reportProgress(hires, cancel);
}
public void initialize(int previewWidth, int previewHeight, int bufSize) {
mPreviewWidth = previewWidth;
mPreviewHeight = previewHeight;
mPreviewBufferSize = bufSize;
setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize);
setStripType(Mosaic.STRIPTYPE_WIDE);
reset();
}
public void clear() {
if (mIsMosaicMemoryAllocated) {
mMosaicer.freeMosaicMemory();
mIsMosaicMemoryAllocated = false;
}
synchronized (this) {
notify();
}
}
public boolean isMosaicMemoryAllocated() {
return mIsMosaicMemoryAllocated;
}
public void setStripType(int type) {
mMosaicer.setStripType(type);
}
private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) {
Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize);
if (mIsMosaicMemoryAllocated) throw new RuntimeException("MosaicFrameProcessor in use!");
mIsMosaicMemoryAllocated = true;
mMosaicer.allocateMosaicMemory(previewWidth, previewHeight);
}
public void reset() {
// reset() can be called even if MosaicFrameProcessor is not initialized.
// Only counters will be changed.
mTotalFrameCount = 0;
mFillIn = 0;
mLastProcessedFrameTimestamp = 0;
mTotalTranslationX = 0;
mTranslationLastX = 0;
mTotalTranslationY = 0;
mTranslationLastY = 0;
mTotalDeltaTime = 0;
mPanningRateX = 0;
mPanningRateY = 0;
mLastProcessFrameIdx = -1;
mCurrProcessFrameIdx = -1;
for (int i = 0; i < WINDOW_SIZE; ++i) {
mDeltaX[i] = 0f;
mDeltaY[i] = 0f;
mDeltaTime[i] = 0f;
}
mMosaicer.reset();
}
public int createMosaic(boolean highRes) {
return mMosaicer.createMosaic(highRes);
}
public byte[] getFinalMosaicNV21() {
return mMosaicer.getFinalMosaicNV21();
}
// Processes the last filled image frame through the mosaicer and
// updates the UI to show progress.
// When done, processes and displays the final mosaic.
public void processFrame() {
if (!mIsMosaicMemoryAllocated) {
// clear() is called and buffers are cleared, stop computation.
// This can happen when the onPause() is called in the activity, but still some frames
// are not processed yet and thus the callback may be invoked.
return;
}
long t1 = System.currentTimeMillis();
mFrameTimestamp[mFillIn] = t1;
mCurrProcessFrameIdx = mFillIn;
mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER);
// Check that we are trying to process a frame different from the
// last one processed (useful if this class was running asynchronously)
if (mCurrProcessFrameIdx != mLastProcessFrameIdx) {
mLastProcessFrameIdx = mCurrProcessFrameIdx;
// Access the timestamp associated with it...
long timestamp = mFrameTimestamp[mCurrProcessFrameIdx];
// TODO: make the termination condition regarding reaching
// MAX_NUMBER_OF_FRAMES solely determined in the library.
if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) {
// If we are still collecting new frames for the current mosaic,
// process the new frame.
calculateTranslationRate(timestamp);
// Publish progress of the ongoing processing
if (mProgressListener != null) {
mProgressListener.onProgress(false, mPanningRateX, mPanningRateY,
mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
}
} else {
if (mProgressListener != null) {
mProgressListener.onProgress(true, mPanningRateX, mPanningRateY,
mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
}
}
}
}
public void calculateTranslationRate(long now) {
float[] frameData = mMosaicer.setSourceImageFromGPU();
int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX];
mTotalFrameCount = (int) frameData[FRAME_COUNT_INDEX];
float translationCurrX = frameData[X_COORD_INDEX];
float translationCurrY = frameData[Y_COORD_INDEX];
if (mLastProcessedFrameTimestamp == 0f) {
// First time: no need to update delta values.
mTranslationLastX = translationCurrX;
mTranslationLastY = translationCurrY;
mLastProcessedFrameTimestamp = now;
return;
}
// Moving average: remove the oldest translation/deltaTime and
// add the newest translation/deltaTime in
int idx = mOldestIdx;
mTotalTranslationX -= mDeltaX[idx];
mTotalTranslationY -= mDeltaY[idx];
mTotalDeltaTime -= mDeltaTime[idx];
mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX);
mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY);
mDeltaTime[idx] = (now - mLastProcessedFrameTimestamp) / 1000.0f;
mTotalTranslationX += mDeltaX[idx];
mTotalTranslationY += mDeltaY[idx];
mTotalDeltaTime += mDeltaTime[idx];
// The panning rate is measured as the rate of the translation percentage in
// image width/height. Take the horizontal panning rate for example, the image width
// used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR).
// To get the horizontal translation percentage, the horizontal translation,
// (translationCurrX - mTranslationLastX), is divided by the
// image width. We then get the rate by dividing the translation percentage with deltaTime.
mPanningRateX = mTotalTranslationX /
(mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / mTotalDeltaTime;
mPanningRateY = mTotalTranslationY /
(mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / mTotalDeltaTime;
mTranslationLastX = translationCurrX;
mTranslationLastY = translationCurrY;
mLastProcessedFrameTimestamp = now;
mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE;
}
}