2015-01-29 3 views
1

Моя проблема заключается в следующем: я хочу получить фоновый сервис, который будет получать кадры из камеры в реальном времени, чтобы я мог их анализировать. Я видел здесь много подобных тем, которые предположительно рассматривают эту проблему, но ни один из них не работал в моем случае.Захват кадров камеры в фоновом режиме (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 дня и не добился успеха.

Справка была бы принята с благодарностью.

+0

Вы пытаетесь отобразить кадры или просто захватить их? Если вам не нужно отображать их, - я бы не подумал, что вы хотите «захватить фон» - избавиться от чего-либо со словом «Вид» в названии. Используйте внешнюю поверхность в качестве места назначения для рендеринга из SurfaceTexture. См. Grafika (https://github.com/google/grafika) для некоторых примеров работы с камерой и SurfaceTexture (возможно, «непрерывный захват»). – fadden

+0

Я посмотрел на ContinuousCaptureActivity, и я сделал его в службу, однако ... как только я нажимаю Back или Home, вызывается surfaceDestroyed, и из-за этого непрерывный захват останавливается. Кажется, я не могу продолжить, даже после того, как программа будет помещена в фоновом режиме. Мне особенно интересно узнать, как я могу использовать внешнюю поверхность в качестве места для рендеринга, как вы упомянули. Любые примеры? – NoelAramis

+0

Один пример можно найти в TextureAploadActivity.java от Grafika - обратите внимание, как OffscreenSurface используется в 'runTextureTest()'. Тест загружает и визуализирует набор текстур несколько раз, чтобы попытаться определить скорость загрузки текстуры, и он делает все за пределами экрана. Код для доступа к пикселям через 'glReadPixels()' включен в конце, где он сохраняет последнюю итерацию для отладки. – fadden

ответ

1

Подведение итогов комментариев: направьте вывод камеры на SurfaceTexture, которая не привязана к объекту View. TextureView будет уничтожен, когда действие приостановлено, освободив его SurfaceTexture, но если вы создадите отдельную SurfaceTexture (или отделите ее от TextureView), на нее не повлияют изменения состояния Activity. Текстура может быть отображена на экранной поверхности, из которой можно считывать пиксели.

Различные примеры можно найти в Grafika.

+1

Я бы это сделал, если бы это было более полезно, чем «посмотреть там» ...>.> – Managarm

+0

Есть ли какой-нибудь пример кода для запуска SurfaceTexure в фоновом режиме? – Dania

+0

@ Дания: Я не уверен, что означает «запустить SurfaceTexture в фоновом режиме». Вы должны написать новый вопрос, объясняющий, что вы пытаетесь сделать, и то, что вы пробовали до сих пор. – fadden