Android: Kamera-Asynctask mit Vorschau Rückruf
Habe ich es geschafft, die Kamera-Vorschau mit benutzerdefinierten filter (Graustufen, Farbton, usw) arbeiten. Diese custom-filter mit Vorschau Rückruf durch die Manipulation der array von RGB und dann zeichnen Sie zurück auf eine Leinwand, dann auf der Oberfläche anzeigen.
Der Nachteil von diesem ist, bekomme ich eine sehr niedrige FPS. Mit dieser niedrigen FPS, es macht zu viel Arbeit in der UI-thread, wenn ich nicht dazu in der hintergrund-thread mit Asynctask. Also habe ich versucht zu verwenden, Asynctask für die Bedienung einer Kamera (mein Hauptzweck ist, um die UI noch immer perfekt funktioniert, auch mit der schweren Arbeit von der Kamera, Vorschau, Rückruf).
Aber auch nachdem ich Asynctask verwendet, es half nicht viel. So Frage ich mich, ist es meine Implementierung falsch ist, oder ist es da auch mit asynctask im UI-thread wird immer noch betroffen sein?
Ausschnitt von meinem code ist unten:
CameraActivity.java
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("ACTIVITY_LIFECYCLE","CameraActivity: onCreate");
setContentView(R.layout.camera_layout);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onResume() {
Log.d("ACTIVITY_LIFECYCLE","CameraActivity: onResume");
if(preview == null){
preview = new CameraPreviewAsync(this,camera);
preview.execute();
}
super.onResume();
}
@Override
protected void onPause() {
Log.d("ACTIVITY_LIFECYCLE","CameraActivity: onPause");
if(preview!=null){
preview.cancel(true);
camera = preview.getCamera();
if(camera!=null){
camera.stopPreview();
camera.setPreviewCallback(null);
camera.release();
camera = null;
preview.setCamera(camera);
}
preview = null;
}
super.onPause();
}
@Override
public void onDestroy(){
Log.d("ACTIVITY_LIFECYCLE","CameraActivity: onDestroy");
super.onDestroy();
}
CameraPreviewAsync.java:
private final String TAG = "CameraPreviewAsync";
private CameraActivity camAct;
private Camera mCamera;
private int cameraId;
private SurfaceView mSurfaceView;
private SurfaceHolder mHolder;
private boolean isPreviewRunning = false;
private int[] rgbints;
private int width;
private int height;
private Bitmap mBitmap;
public CameraPreviewAsync(CameraActivity act, Camera cam){
this.camAct = act;
this.mCamera = cam;
this.mSurfaceView = (SurfaceView) act.findViewById(R.id.surfaceView);
}
public void resetSurface(){
if(mCamera!=null){
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
int tempId = R.id.surfaceView;
RelativeLayout buttonBar = (RelativeLayout) camAct.findViewById(R.id.buttonBar);
((RelativeLayout) camAct.findViewById(R.id.preview)).removeAllViews();
SurfaceView newSurface = new SurfaceView(camAct);
newSurface.setId(tempId);
RelativeLayout.LayoutParams layParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
layParams.alignWithParent = true;
newSurface.setLayoutParams(layParams);
((RelativeLayout) camAct.findViewById(R.id.preview)).addView(newSurface);
((RelativeLayout) camAct.findViewById(R.id.preview)).addView(buttonBar);
}
@Override
protected void onPreExecute() {
//Things to do before doInBackground executed
Log.d(TAG,"onPreExecute");
RelativeLayout.LayoutParams layParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
layParams.alignWithParent = true;
mSurfaceView.setLayoutParams(layParams);
//Check number of camera in the device, if less than 2 then remove swap button
if (Camera.getNumberOfCameras() < 2) {
((RelativeLayout) camAct.findViewById(R.id.buttonBar)).removeViewAt(R.id.cameraSwap);
}
//Opening the camera
cameraId = findBackFacingCamera();
if (cameraId < 0) {
cameraId = findFrontFacingCamera();
if (cameraId < 0)
Toast.makeText(camAct, "No camera found.", Toast.LENGTH_LONG).show();
else
mCamera = Camera.open(cameraId);
} else {
mCamera = Camera.open(cameraId);
}
//invalidate the menu bar and show menu appropriately
camAct.invalidateOptionsMenu();
//get Camera parameters and set it to Auto Focus
if(mCamera!=null){
Camera.Parameters params = mCamera.getParameters();
List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
//set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
//set Camera parameters
mCamera.setParameters(params);
}
}
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... params) {
//Things to do in the background thread
Log.d(TAG,"doInBackground");
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(surfaceCallback);
return null;
}
@Override
protected void onPostExecute(Void values) {
//Things to do after doInBackground
Log.d(TAG,"onPostExecute");
}
@Override
protected void onCancelled(){
super.onCancelled();
}
/*
* ************************************************************************************
* SURFACEHOLDER CALLBACK
* ************************************************************************************
*/
SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG,"surfaceCreated!!");
if(CameraActivity.filterMode == CameraActivity.NORMAL_FILTER){
try {
if (mCamera != null) {
mCamera.startPreview();
mCamera.setPreviewDisplay(holder);
}else{
Log.d(TAG,"CAMERA IS NULL in surfaceCreated!!");
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}else{
synchronized(mSurfaceView){
if(isPreviewRunning){
return;
}else{
mSurfaceView.setWillNotDraw(false);
if(mCamera!=null){
isPreviewRunning = true;
Camera.Parameters p = mCamera.getParameters();
List<Size> sizes = p.getSupportedPreviewSizes();
Size size = p.getPreviewSize();
width = size.width;
height = size.height;
p.setPreviewFormat(ImageFormat.NV21);
showSupportedCameraFormats(p);
mCamera.setParameters(p);
rgbints = new int[width * height];
mCamera.startPreview();
mCamera.setPreviewCallback(previewCallback);
}
}
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG,"surfaceDestroyed!");
if(CameraActivity.filterMode == CameraActivity.NORMAL_FILTER){
if (mCamera != null) {
mCamera.stopPreview();
isPreviewRunning = false;
}
}else{
synchronized(mSurfaceView){
if(mCamera!=null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
isPreviewRunning = false;
}
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.d(TAG,"surfaceChanged!");
}
};
/*
* ************************************************************************************
* CAMERA PREVIEW CALLBACK
* ************************************************************************************
*/
Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (!isPreviewRunning)
return;
Canvas resCanvas = null;
if (mHolder == null) {
return;
}
try {
synchronized (mHolder) {
resCanvas = mHolder.lockCanvas(null);
int resCanvasW = resCanvas.getWidth();
int resCanvasH = resCanvas.getHeight();
if(mBitmap == null){
mBitmap = Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);
}
decodeYUV(rgbints, data, width, height);
Canvas canvas = new Canvas(mBitmap);
//Setting the filter
if(camAct.getCustomFilter().equalsIgnoreCase("NORMAL")) ;//don't change the rgb value
if(camAct.getCustomFilter().equalsIgnoreCase("GRAYSCALE")) rgbints = grayscale(rgbints);
if(camAct.getCustomFilter().equalsIgnoreCase("INVERT")) rgbints = invert(rgbints);
if(camAct.getCustomFilter().equalsIgnoreCase("BOOSTRED")) rgbints = boostColor(rgbints,1);
if(camAct.getCustomFilter().equalsIgnoreCase("BOOSTGREEN")) rgbints = boostColor(rgbints,2);
if(camAct.getCustomFilter().equalsIgnoreCase("BOOSTBLUE")) rgbints = boostColor(rgbints,3);
if(camAct.getCustomFilter().equalsIgnoreCase("NOISE")) rgbints = noise(rgbints);
if(camAct.getCustomFilter().equalsIgnoreCase("HUE")) rgbints = hue(rgbints);
if(camAct.getCustomFilter().equalsIgnoreCase("SATURATION")) rgbints = saturation(rgbints);
if(camAct.getCustomFilter().equalsIgnoreCase("ENGRAVE")) rgbints = engrave(rgbints);
if(camAct.getCustomFilter().equalsIgnoreCase("EMBOSS")) rgbints = emboss(rgbints);
//draw the decoded image, centered on canvas
canvas.drawBitmap(rgbints, 0, width, 0,0, width, height, false, null);
resCanvas.drawBitmap (mBitmap, resCanvasW-((width+resCanvasW)>>1), resCanvasH-((height+resCanvasH)>>1),null);
}
} catch (Exception e){
e.printStackTrace();
} finally {
//do this in a finally so that if an exception is thrown
//during the above, we don't leave the Surface in an
//inconsistent state
if (resCanvas != null) {
mHolder.unlockCanvasAndPost(resCanvas);
}
}
}
};
Jede Hilfe ist sehr willkommen! 🙂 Vielen Dank im Voraus Jungs!
nope habe ich nicht. Ich bin mir nicht sicher, wie es zu tun, können Sie mir empfehlen ein Weg, um es zu testen? danke!
InformationsquelleAutor CodingBird | 2013-10-07
Du musst angemeldet sein, um einen Kommentar abzugeben.
Rückrufe aus anderen Methoden geliefert werden, die Ereignis-Schleife des Fadens, die sogenannte open(). Wenn dieser thread hat keine Ereignis-Schleife, dann werden Rückrufe geliefert, um die Haupt-Anwendung-Ereignis-Schleife. Wenn es keine Haupt-Anwendung-Ereignis-Schleife, Rückrufe werden nicht geliefert. Quelle
Ich habe lange gesucht Möglichkeiten, um das Kamera-Vorschau mit openGL, aber ich kann nicht finden, eine gute, auch wenn ich eines festgestellt habe, wenn ich es versuchte, ich kann nicht für die benutzerdefinierten filter. Können Sie schlagen einige Möglichkeiten? danke Mann!
Nur ein FYI: Aus der Android-Dokumentation, onPreviewFrame heißt auf den thread, erwirbt der Kamera Kamera verwenden.open().
leider sind Sie falsch und Boston Walker hat Recht: Rückrufe von anderen Methoden geliefert werden, die Ereignis-Schleife des Fadens, die sogenannte open(). Wenn dieser thread hat keine Ereignis-Schleife, dann werden Rückrufe geliefert, um die Haupt-Anwendung-Ereignis-Schleife. Wenn es keine Haupt-Anwendung-Ereignis-Schleife, Rückrufe werden nicht geliefert.
https://developer.android.com/reference/android/hardware/Camera.PreviewCallback.html#onPreviewFrame(byte[], android.hardware.Camera)
Marquis ja, ich vermisste versuchen mit Hilfe von event-Greifer. Ich passe meine Antwort.
InformationsquelleAutor Daud Arfin
Vielleicht meine Antwort ist zu spät für dich, aber ich recherchiere das gleiche Thema, also dachte ich würde teilen meine Erkenntnisse trotzdem...
Zuerst, wenn die Kamera "öffnen" wird aufgerufen, auf dem AsyncTask und dann, der thread existiert und greift auf zu existieren - wir können nicht wirklich erwarten, Rückrufe zu kommen, können wir. Also, wenn wir wollen, Rückrufe, dann müssen wir einen thread, der lebt mindestens so lange wie wir wollen, dass unsere Rückrufe.
Aber Wartezeit, dort ist mehr... Dokumentation für die Kamera.PreviewCallback ist nicht das klarste, aber einer von den schlechten Tipps ist dieser "Dieser callback wird aufgerufen, auf der Veranstaltung thread öffnen(int) aufgerufen wurde." Was bedeuten Sie durch "Ereignis" - thread? Gut, es ist nicht ganz klar - aber ein Blick in den Android-code und Experimentieren - was Sie brauchen, ist ein thread, der enthält einen Greifer. Wahrscheinlich zu viel details, aber in der Kamera-Konstruktor (das heißt von der open-Methode) es gibt code, der versucht, sich zunächst ein Looper, der den aktuellen thread, wenn das nicht vorhanden ist - es versucht, sich Haupt-thread "looper" - das Leben auf dem UI-thread. Die Kamera verwendet dann einen Handler ein, der Versand Rückrufe und andere Methoden, über die Greifer, der es initialisiert so. Jetzt können Sie wahrscheinlich sehen, warum Sie immer waren Ihren Anruf Rücken auf den Haupt-thread, obwohl Sie öffnete Kamera aus einem anderen thread - Ihr worker-thread nicht ein "looper" - also Kamera standardmäßig auf die main.
Bekam ich Rückrufe arbeiten aus meiner worker-thread für die, die ich verwendet HandlerThread in einer Methode, die entlang dieser Linien:
...
Ich verwendet, debugger, um zu bestätigen, dass meine onPreviewFrame lief auf den worker-thread. Ich hatte auch animation läuft auf dem UI-thread, das war jerky, bevor ich umgestellt frame-Verarbeitung Weg von der main-thread, aber jetzt ist es so glatt wie butter.
Tun, beachten Sie, dass wenn Sie töten Ihre worker-thread, der dann natürlich Ihre Rückrufe werden ebenfalls aufhören und Kamera (auch eher Hf) wird sich beschweren, dass Sie versuchen, mit Toten thread.
BTW, als alternative Lösung, die man natürlich hatte, konnte callbacks aufgerufen werden, auf dem Haupt-thread, aber die Verarbeitung von frame-Daten können übertragen werden, um einen separaten thread.
Ist es möglich, wenn Sie @leonman30 könnte die post Ihre gesamte Aktivität der Kamera + Klassen unterstützen? Ich bin auf der Suche nach einem netten fluid nicht hacky-Kamera-Lösung.. aber ich block immer im UI-thread 😀
InformationsquelleAutor leonman30
Ich denke mal, deine Umsetzung mit
AsyncTask
ist falsch:Laut der Dokumentation, Kamera callbacks werden aufgerufen, auf dem thread, der genannt
open()
. Und so ist dieonPreviewFrame
Rückruf. (Daher ist es nicht wahr, dassonPreviewFrame
ist immer auf dem Hauptthread ausgeführt.)Sind Sie öffnen die Kamera in der
AsyncTask
's onPreExecute() Methode, die aufgerufen wird auf dem UI-thread und nicht auf den hintergrund-thread wie Sie wahrscheinlich erwartet, und daher die Kamera callback ausgeführt werden, auf dem Haupt-thread.Ich nehme an, Sie sollten öffnen Sie die Kamera in die AsyncTask ist
doInBackground()
Methode.InformationsquelleAutor Filip J.