Domanda

Sto scrivendo un 1.5 un'applicazione Android che inizia subito dopo il boot-up. Si tratta di un Service e dovrebbe scattare una foto, senza anteprima. Questa applicazione registrerà la densità della luce in alcune zone qualunque. Sono stato in grado di scattare una foto, ma la foto è stata nera.

Dopo la ricerca per un lungo periodo di tempo, mi sono imbattuto in un thread bug su di esso. Se non si genera un'anteprima, l'immagine sarà nera poiché fotocamera Android ha bisogno di anteprima per l'esposizione di installazione e messa a fuoco. Ho creato un SurfaceView e l'ascoltatore, ma l'evento onSurfaceCreated() non viene licenziato.

Credo che la ragione è, non è in fase di creazione della superficie visivamente. Ho visto anche alcuni esempi di chiamare la fotocamera in modo statico con MediaStore.CAPTURE_OR_SOMETHING che prende una foto e salva nella cartella desiderata con due righe di codice, ma non ci vuole un quadro troppo.

Do necessità di utilizzare IPC e bindService() chiamare questa funzione io? O c'è un metodo alternativo per raggiungere questo obiettivo?

È stato utile?

Soluzione

è davvero strano che macchina fotografica su piattaforma Android non può streaming video fino a quando non dato superficie anteprima valido. sembra che gli architetti della piattaforma non stava pensando a terze parti di video in streaming applicazioni a tutti. anche per caso la realtà aumentata l'immagine può essere presentato come una sorta di sostituzione visiva, non in tempo reale flusso della macchina fotografica.

In ogni caso, si può semplicemente ridimensionamento anteprima superficie di 1x1 pixel e metterlo da qualche parte in un angolo del widget (elemento visivo). Si prega di prestare attenzione -. ridimensionamento anteprima superficie, non dimensione del frame fotocamera

Naturalmente tale trucco non eliminare i dati indesiderati in streaming (per l'anteprima) che consuma alcune risorse di sistema e della batteria.

Altri suggerimenti

Ho trovato la risposta a questa nel Android fotocamera Docs .

  

Nota: E 'possibile utilizzare MediaRecorder senza creare una macchina fotografica   un'anteprima prima e saltare i primi passi di questo processo. Però,   dal momento che gli utenti in genere preferiscono vedere in anteprima prima di iniziare un   la registrazione, questo processo non è discusso qui.

È possibile trovare il passo per passo le istruzioni al link qui sopra. Dopo le istruzioni, sarà stato la citazione che ho fornito sopra.

In realtà è possibile, ma è necessario falsificare l'anteprima con un manichino SurfaceView

SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);

Aggiornamento 9/21/11:. A quanto pare questo non funziona per tutti i dispositivi Android

di scattare la foto

ottenere questo lavoro prima di cercare di nascondere l'anteprima.

  • Impostare correttamente l'anteprima
    • Usa un SurfaceView (pre-Android-4,0 compatibilità) o SurfaceTexture (Android 4+, può essere reso trasparente)
    • Imposta e inizializzare prima di scattare la foto
    • Attendere SurfaceView del SurfaceHolder (via getHolder()) per rapporto surfaceCreated() o TextureView al rapporto onSurfaceTextureAvailable alla sua SurfaceTextureListener prima di impostare e inizializzare l'anteprima.
  • Assicurarsi che l'anteprima è visibile:
    • Aggiungi al WindowManager
    • Verificare la sua dimensione layout è di almeno 1x1 pixel (si potrebbe desiderare di iniziare rendendo MATCH_PARENT x MATCH_PARENT per il test)
    • Assicurarsi che la sua visibilità è View.VISIBLE (che sembra essere l'impostazione predefinita se non si specifica di esso)
    • Assicurarsi di utilizzare il FLAG_HARDWARE_ACCELERATED nel LayoutParams se è un TextureView.
  • richiamata JPEG Usa takePicture dal momento che la documentazione dice le altre callback non sono supportati su tutti i dispositivi

Risoluzione dei problemi

  • Se surfaceCreated / onSurfaceTextureAvailable non viene chiamato, probabilmente non viene visualizzata la SurfaceView / TextureView.
  • Se takePicture fallisce, prima assicurarsi l'anteprima funziona correttamente. È possibile rimuovere la chiamata takePicture e consentire l'esecuzione di anteprima per vedere se si visualizza sullo schermo.
  • Se l'immagine è più scura di quella che dovrebbe essere, potrebbe essere necessario ritardare per circa un secondo prima di chiamare takePicture modo che la telecamera ha tempo per regolare l'esposizione, una volta l'anteprima è iniziato.

Nascondere l'Anteprima

  • Fare la dimensione anteprima View 1x1 per ridurre al minimo la sua visibilità ( o provare 8x16 per forse più affidabilità )

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • Sposta l'anteprima fuori dal centro di ridurre la sua rilevabilità:

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • Fare l'anteprima trasparente (funziona solo per TextureView)

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        width, height, /*...*/
        PixelFormat.TRANSPARENT);
    params.alpha = 0;
    

Esempio di lavoro (testato su Sony Xperia M, Android 4.3)

/** Takes a single photo on service start. */
public class PhotoTakingService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        takePhoto(this);
    }

    @SuppressWarnings("deprecation")
    private static void takePhoto(final Context context) {
        final SurfaceView preview = new SurfaceView(context);
        SurfaceHolder holder = preview.getHolder();
        // deprecated setting, but required on Android versions prior to 3.0
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        holder.addCallback(new Callback() {
            @Override
            //The preview must happen at or after this point or takePicture fails
            public void surfaceCreated(SurfaceHolder holder) {
                showMessage("Surface created");

                Camera camera = null;

                try {
                    camera = Camera.open();
                    showMessage("Opened camera");

                    try {
                        camera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }

                    camera.startPreview();
                    showMessage("Started preview");

                    camera.takePicture(null, null, new PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            showMessage("Took picture");
                            camera.release();
                        }
                    });
                } catch (Exception e) {
                    if (camera != null)
                        camera.release();
                    throw new RuntimeException(e);
                }
            }

            @Override public void surfaceDestroyed(SurfaceHolder holder) {}
            @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
        });

        WindowManager wm = (WindowManager)context
            .getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1, 1, //Must be at least 1x1
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
                //Don't know if this is a safe default
                PixelFormat.UNKNOWN);

        //Don't set the preview visibility to GONE or INVISIBLE
        wm.addView(preview, params);
    }

    private static void showMessage(String message) {
        Log.i("Camera", message);
    }

    @Override public IBinder onBind(Intent intent) { return null; }
}

Su Android 4.0 e versioni successive (livello di API> = 14), è possibile utilizzare TextureView per anteprima il flusso della macchina fotografica e fare in modo invisibile per non vederlo per l'utente. Ecco come:

Per prima cosa creare una classe per implementare un SurfaceTextureListener che otterrà Crea / callback di aggiornamento per la superficie di anteprima. Questa classe prende anche un oggetto fotocamera come input, in modo che possa chiamare funzione startPreview della fotocamera non appena viene creata la superficie:

public class CamPreview extends TextureView implements SurfaceTextureListener {

  private Camera mCamera;

  public CamPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;
   }

  @Override
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
    setLayoutParams(new FrameLayout.LayoutParams(
        previewSize.width, previewSize.height, Gravity.CENTER));

    try{
      mCamera.setPreviewTexture(surface);
     } catch (IOException t) {}

    mCamera.startPreview();
    this.setVisibility(INVISIBLE); // Make the surface invisible as soon as it is created
  }

  @Override
  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
      // Put code here to handle texture size change if you want to
  }

  @Override
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    return true;
  }

  @Override
  public void onSurfaceTextureUpdated(SurfaceTexture surface) {
      // Update your view here!
  }
}

Sono necessari per implementare una classe di callback per elaborare i dati di anteprima:

public class CamCallback implements Camera.PreviewCallback{
  public void onPreviewFrame(byte[] data, Camera camera){
     // Process the camera data here
  }
}

Utilizzare il sopra delle classi CamPreview e CamCallback per impostare la fotocamera in onCreate della vostra attività () o la funzione di avvio simile:

// Setup the camera and the preview object
Camera mCamera = Camera.open(0);
CamPreview camPreview = new CamPreview(Context,mCamera);
camPreview.setSurfaceTextureListener(camPreview);

// Connect the preview object to a FrameLayout in your UI
// You'll have to create a FrameLayout object in your UI to place this preview in
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraView); 
preview.addView(camPreview);

// Attach a callback for preview
CamCallback camCallback = new CamCallback();
mCamera.setPreviewCallback(camCallback);

C'è un modo di fare questo, ma è un po 'complicato. cosa si dovrebbe fare, è allegare un surfaceholder per il window manager del servizio

WindowManager wm = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);        
wm.addView(surfaceview, params);

e quindi impostare

surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);

dove mHolder è il titolare che si ottiene dalla superficie vista.

in questo modo, si può giocare con alfa del SurfaceView, renderla completamente trasparente, ma la fotocamera ancora ottenere fotogrammi.

che è come lo faccio. Spero che aiuta:)

Abbiamo risolto questo problema utilizzando un SurfaceView fittizio (non aggiunto alla GUI reale) nelle versioni inferiori a 3.0 (o diciamo 4.0 come servizio fotocamera su una tavoletta in realtà non ha senso). Nelle versioni> = 4.0 questo ha funzionato solo l'emulatore; ( L'uso di SurfaceTexture (e setSurfaceTexture ()) invece di SurfaceView (e setSurfaceView ()) ha lavorato qui. Almeno questo funziona su Nexus S.

Credo che questo è davvero un difetto del quadro Android.

Nel "Esempio di lavoro di Sam" (Grazie Sam ...)

se alle istruzioni d' "wm.addView (anteprima, params);"

ottenere eccezione "Impossibile aggiungere finestra android.view.ViewRoot - permesso negato per questo tipo di finestra"

risolvere utilizzando questa autorizzazione in AndroidManifest:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Si può provare questo codice di lavoro, questo servizio clicca immagine anteriore, se si desidera che la cattura dell'immagine della telecamera allora backCamera rimuovere il commento nel codice e commentare frontCamera.

Nota: - Consenti fotocamera e il permesso Storage per app e StartService da attività o dovunque.

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        CapturePhoto();
    }

    private void CapturePhoto() {

        Log.d("kkkk","Preparing to take photo");
        Camera camera = null;

        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

            int frontCamera = 1;
            //int backCamera=0;

            Camera.getCameraInfo(frontCamera, cameraInfo);

            try {
                camera = Camera.open(frontCamera);
            } catch (RuntimeException e) {
                Log.d("kkkk","Camera not available: " + 1);
                camera = null;
                //e.printStackTrace();
            }
            try {
                if (null == camera) {
                    Log.d("kkkk","Could not get camera instance");
                } else {
                    Log.d("kkkk","Got the camera, creating the dummy surface texture");
                     try {
                         camera.setPreviewTexture(new SurfaceTexture(0));
                        camera.startPreview();
                    } catch (Exception e) {
                        Log.d("kkkk","Could not set the surface preview texture");
                        e.printStackTrace();
                    }
                    camera.takePicture(null, null, new Camera.PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            File pictureFileDir=new File("/sdcard/CaptureByService");

                            if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
                                pictureFileDir.mkdirs();
                            }
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
                            String date = dateFormat.format(new Date());
                            String photoFile = "ServiceClickedPic_" + "_" + date + ".jpg";
                            String filename = pictureFileDir.getPath() + File.separator + photoFile;
                            File mainPicture = new File(filename);

                            try {
                                FileOutputStream fos = new FileOutputStream(mainPicture);
                                fos.write(data);
                                fos.close();
                                Log.d("kkkk","image saved");
                            } catch (Exception error) {
                                Log.d("kkkk","Image could not be saved");
                            }
                            camera.release();
                        }
                    });
                }
            } catch (Exception e) {
                camera.release();
            }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top