Camera frames are never captured in background service (Android)

匿名 (未验证) 提交于 2019-12-03 03:03:02

问题:

I want to run the front camera of a device from a background service, getting the captured frames by the camera in the background service using the callback that's called whenever a new frame is captured by the camera (eg, onPreviewFrame), and apply some real time processing on the obtained frames.

After searching I understand that there are two ways to run the camera in the background:

1- setting a SurfaceView with size 1 X 1.

2- Using SurfaceTexture (This does not require any view which seems more efficient.)

I tried both ways, I could only run the camera from the background, but the callbacks for getting frames were never called, though I tried different tricks.

The code I used for method 1 is available here:

https://stackoverflow.com/questions/35160911/why-onpreviewframe-is-not-being-called-from-a-background-service

Note that I've also tried to call onPreviewFrame inside surfaceChanged, but I always get that the camera is null when trying to use it to set the callback! I even tried to re-instantiate it as camera = Camer.open(), but I got an error saying "Fail to connect to camera service."

Here is the code for method 2:

public class BackgroundService extends Service implements TextureView.SurfaceTextureListener {      private Camera camera = null;     private int  NOTIFICATION_ID= 1;     private static final String TAG = "OCVSample::Activity";     // Binder given to clients     private final IBinder mBinder = new LocalBinder();     private WindowManager windowManager;     private SurfaceTexture mSurfaceTexture= new SurfaceTexture (10);       public class LocalBinder extends Binder {         BackgroundService getService() {             // Return this instance of this service so clients can call public methods             return BackgroundService.this;         }     }//end inner class that returns an instance of the service.      @Override     public IBinder onBind(Intent intent) {          return mBinder;     }//end onBind.       @Override     public void onCreate() {          // Start foreground service to avoid unexpected kill          startForeground(NOTIFICATION_ID, buildNotification());       }        private Notification buildNotification () {          NotificationCompat.Builder notificationBuilder=new NotificationCompat.Builder(this);          notificationBuilder.setOngoing(true); //this notification should be ongoing          notificationBuilder.setContentTitle(getString(R.string.notification_title))                 .setContentText(getString (R.string.notification_text_and_ticker))                 .setSmallIcon(R.drawable.vecsat_logo)                 .setTicker(getString(R.string.notification_text_and_ticker));         return(notificationBuilder.build());      }       @Override     public void onDestroy() {          Log.i(TAG, "surfaceDestroyed method");       }      /***********Ok now all service related methods were implemented**************/      /************now implement surface texture related methods*****************/       @Override     public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {          //Now the surfaceTexture available, so we can create the camera.         camera = Camera.open(1);         mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {             @Override             public void onFrameAvailable(SurfaceTexture surfaceTexture) {                 Log.e(TAG, "frame captured");              }         });         //now try to set the preview texture of the camera which is actually the  surfaceTexture that has just been created.          try {             camera.setPreviewTexture(mSurfaceTexture);         }          catch (IOException e){             Log.e(TAG, "Error in setting the camera surface texture");         }          camera.setPreviewCallback(new Camera.PreviewCallback() {             @Override             public void onPreviewFrame(byte[] bytes, Camera camera) {                 Log.e(TAG, "frame captured");              }         });         // the preview was set, now start it            camera.startPreview();      }      @Override     public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {         //super implementation is sufficient.     }      @Override     public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {         //stop the preview.         camera.stopPreview();         //release camera resource:         camera.release();         return false;     }      @Override     public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {          mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {             @Override             public void onFrameAvailable(SurfaceTexture surfaceTexture) {                 Log.e(TAG, "frame captured from update");              }         });         camera.stopPreview(); //stop current preview.         try {             camera.setPreviewTexture(mSurfaceTexture);         }          catch (IOException e){             Log.e(TAG, "Error in setting the camera surface texture");          }          camera.setPreviewCallback(new Camera.PreviewCallback() {             @Override             public void onPreviewFrame(byte[] bytes, Camera camera) {                 Log.e(TAG, "frame captured");              }         });          camera.startPreview(); // start the preview again.       } 

Here is how I call the service from an activity:

@Override     protected void onCreate(Bundle savedInstanceState) {         //call the method in the super class..         super.onCreate(savedInstanceState);         //inflate the xml layout.         setContentView(R.layout.main);          //prepare buttons.          ImageButton startButton = (ImageButton) findViewById(R.id.start_btn);         ImageButton stopButton = (ImageButton) findViewById(R.id.stop_btn);          //prepare service intent:         final Intent serviceIntent = new Intent(getApplicationContext(), BackgroundService.class);          //set on click listeners for buttons (this should respond to normal touch events and simulated ones):            startButton.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                  //startActivity(serviceIntent);                 //start eye gazing mode as a service running in the background:               isBound= getApplicationContext().bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);              }         });          stopButton.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 //stop eye gazing mode as a service running in the background:                      getApplicationContext().unbindService(mConnection);                }         });      }//end onCreate.      private final ServiceConnection mConnection = new ServiceConnection() {          @Override         public void onServiceConnected(ComponentName className, IBinder service) {              BackgroundService.LocalBinder binder = (BackgroundService.LocalBinder) service;             mService = binder.getService();             isBound = true;         }          @Override         public void onServiceDisconnected(ComponentName className) {             isBound = false;         }     }; 

Please help, how can I get the frames callback to work? I tried a lot but I couldn't figure it out.

Any help is highly appreciated whether it uses method 1 or 2.

Thanks.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!