Моя проблема заключается в следующем: я хочу получить фоновый сервис, который будет получать кадры из камеры в реальном времени, чтобы я мог их анализировать. Я видел здесь много подобных тем, которые предположительно рассматривают эту проблему, но ни один из них не работал в моем случае.Захват кадров камеры в фоновом режиме (Android)
Моя первая попытка состояла в том, чтобы создать Activity, в котором была запущена Служба, и внутри службы я создал surfaceView, из которого я получил держатель и реализовал обратный вызов, в котором я подготовил камеру и все такое. Затем, на previewCallback, я мог бы создать новый поток, который мог бы анализировать данные, которые я получал из метода onPreviewFrame PreviewCallback.
Это работало достаточно хорошо, в то время как у меня была эта служба на переднем плане, но как только я открыл другое приложение (с сервисом, все еще работающим в фоновом режиме), я понял, что предварительный просмотр не был таким, чтобы я мог 'получить от него кадры.
Поиск в Интернете, я узнал, что мог бы решить эту проблему с помощью SurfaceTexture. Таким образом, я создал активность which'd начать свою службу, как это:
public class SurfaceTextureActivity extends Activity {
public static TextureView mTextureView;
public static Vibrator mVibrator;
public static GLSurfaceView mGLView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(this);
mTextureView = new TextureView(this);
setContentView(mTextureView);
try {
Intent intent = new Intent(SurfaceTextureActivity.this, RecorderService.class);
intent.putExtra(RecorderService.INTENT_VIDEO_PATH, "/folder-path/");
startService(intent);
Log.i("ABC", "Start Service "+this.toString()+" + "+mTextureView.toString()+" + "+getWindowManager().toString());
}
catch (Exception e) {
Log.i("ABC", "Exc SurfaceTextureActivity: "+e.getMessage());
}
}
}
И тогда я сделал RecorderService реализовать SurfaceTextureListener, так что я мог бы открыть камеру и делать другие препараты, а затем, возможно, захватить кадры. Моя RecorderService в настоящее время выглядит следующим образом:
public class RecorderService extends Service implements TextureView.SurfaceTextureListener, SurfaceTexture.OnFrameAvailableListener {
private Camera mCamera = null;
private TextureView mTextureView;
private SurfaceTexture mSurfaceTexture;
private float[] mTransformMatrix;
private static IMotionDetection detector = null;
public static Vibrator mVibrator;
@Override
public void onCreate() {
try {
mTextureView = SurfaceTextureActivity.mTextureView;
mTextureView.setSurfaceTextureListener(this);
Log.i("ABC","onCreate");
// startForeground(START_STICKY, new Notification()); - doesn't work
} catch (Exception e) {
Log.i("ABC","onCreate exception "+e.getMessage());
e.printStackTrace();
}
}
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture)
{
//How do I obtain frames?!
// SurfaceTextureActivity.mGLView.queueEvent(new Runnable() {
//
// @Override
// public void run() {
// mSurfaceTexture.updateTexImage();
//
// }
// });
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
int height) {
mSurfaceTexture = surface;
mSurfaceTexture.setOnFrameAvailableListener(this);
mVibrator = (Vibrator)this.getSystemService(VIBRATOR_SERVICE);
detector = new RgbMotionDetection();
int cameraId = 0;
Camera.CameraInfo info = new Camera.CameraInfo();
for (cameraId = 0; cameraId < Camera.getNumberOfCameras(); cameraId++) {
Camera.getCameraInfo(cameraId, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
break;
}
mCamera = Camera.open(cameraId);
Matrix transform = new Matrix();
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
int rotation = ((WindowManager)(getSystemService(Context.WINDOW_SERVICE))).getDefaultDisplay()
.getRotation();
Log.i("ABC", "onSurfaceTextureAvailable(): CameraOrientation(" + cameraId + ")" + info.orientation + " " + previewSize.width + "x" + previewSize.height + " Rotation=" + rotation);
try {
switch (rotation) {
case Surface.ROTATION_0:
mCamera.setDisplayOrientation(90);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.height, previewSize.width, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.height/2, 0);
break;
case Surface.ROTATION_90:
mCamera.setDisplayOrientation(0);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.width/2, 0);
break;
case Surface.ROTATION_180:
mCamera.setDisplayOrientation(270);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.height, previewSize.width, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.height/2, 0);
break;
case Surface.ROTATION_270:
mCamera.setDisplayOrientation(180);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.width/2, 0);
break;
}
mCamera.setPreviewTexture(mSurfaceTexture);
Log.i("ABC", "onSurfaceTextureAvailable(): Transform: " + transform.toString());
mCamera.startPreview();
// mTextureView.setVisibility(0);
mCamera.setPreviewCallback(new PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (data == null) return;
Camera.Size size = mCamera.getParameters().getPreviewSize();
if (size == null) return;
//This is where I start my thread that analyzes images
DetectionThread thread = new DetectionThread(data, size.width, size.height);
thread.start();
}
});
}
catch (Exception t) {
Log.i("ABC", "onSurfaceTextureAvailable Exception: "+ t.getMessage());
}
}
Однако, так же как и в другом случае, так как моя анализирующая нить начинается внутри onSurfaceTextureAvailable, что только тогда, когда текстура там, и не тогда, когда я открываю другой приложение, захват кадра не будет продолжаться, когда я открою что-то еще.
Some идеи показали, что это возможно, но я просто не знаю как. Была идея, что я могу реализовать SurfaceTexture.onFrameAvailable, а затем, как только будет доступен новый фрейм, запустить runnable для запуска в потоке рендеринга (GLSurfaceView.queueEvent (..)) и, наконец, запустить runnable-вызов SurfaceTexture.updateTexImage(). Это то, что я пробовал (он прокомментирован в моем коде), но он не работает, приложение падает, если я это делаю.
Что еще я могу сделать? Я знаю, что это может работать как-то, потому что я видел, как он использовался в таких приложениях, как SpyCameraOS (да, я знаю, что это с открытым исходным кодом, и я смотрел код, но я не мог создать рабочего решения), и я чувствую, что я скучаю только в небольшом фрагменте, но я понятия не имею, что я делаю неправильно. Я занимаюсь этим последние 3 дня и не добился успеха.
Справка была бы принята с благодарностью.
Вы пытаетесь отобразить кадры или просто захватить их? Если вам не нужно отображать их, - я бы не подумал, что вы хотите «захватить фон» - избавиться от чего-либо со словом «Вид» в названии. Используйте внешнюю поверхность в качестве места назначения для рендеринга из SurfaceTexture. См. Grafika (https://github.com/google/grafika) для некоторых примеров работы с камерой и SurfaceTexture (возможно, «непрерывный захват»). – fadden
Я посмотрел на ContinuousCaptureActivity, и я сделал его в службу, однако ... как только я нажимаю Back или Home, вызывается surfaceDestroyed, и из-за этого непрерывный захват останавливается. Кажется, я не могу продолжить, даже после того, как программа будет помещена в фоновом режиме. Мне особенно интересно узнать, как я могу использовать внешнюю поверхность в качестве места для рендеринга, как вы упомянули. Любые примеры? – NoelAramis
Один пример можно найти в TextureAploadActivity.java от Grafika - обратите внимание, как OffscreenSurface используется в 'runTextureTest()'. Тест загружает и визуализирует набор текстур несколько раз, чтобы попытаться определить скорость загрузки текстуры, и он делает все за пределами экрана. Код для доступа к пикселям через 'glReadPixels()' включен в конце, где он сохраняет последнюю итерацию для отладки. – fadden