J'écris une application Android 1.5 qui démarre juste après le démarrage. Il s'agit d'un Service
et devrait prendre une photo sans aperçu. Cette application enregistrera la densité lumineuse dans certains J'ai pu prendre une photo mais la photo était noire.
Après avoir fait des recherches pendant longtemps, je suis tombé sur un fil de bogue à ce sujet. Si vous ne générez pas d'aperçu, l'image sera noire car Android l'appareil photo a besoin d'un aperçu pour configurer l'exposition et la mise au point. J'ai créé un SurfaceView
et l'écouteur, mais l'événement onSurfaceCreated()
n'est jamais déclenché.
Je suppose que la raison en est que la surface n'est pas créée visuellement. J'ai également vu quelques exemples d'appels statiques de l'appareil photo avec MediaStore.CAPTURE_OR_SOMETHING
Qui prend une photo et enregistre dans le dossier souhaité avec deux lignes de code, mais il ne prend pas de photo aussi.
Dois-je utiliser IPC et bindService()
pour appeler cette fonction? Ou existe-t-il une autre méthode pour y parvenir?
il est vraiment bizarre que la caméra sur Android ne puisse pas diffuser de vidéo jusqu'à ce qu'elle ait une surface d'aperçu valide. Il semble que les architectes de la plate-forme ne pensaient pas du tout aux applications de streaming vidéo tierces. même dans le cas de la réalité augmentée, l'image peut être présentée comme une sorte de substitution visuelle, et non comme un flux de caméra en temps réel.
de toute façon, vous pouvez simplement redimensionner la surface d'aperçu à 1x1 pixels et la placer quelque part dans le coin du widget (élément visuel). veuillez faire attention - redimensionnez la surface d'aperçu, pas la taille du cadre de la caméra.
bien sûr, une telle astuce n'élimine pas le streaming de données indésirables (pour l'aperçu) qui consomme certaines ressources système et batterie.
J'ai trouvé la réponse à cela dans le Android Camera Docs .
Remarque: Il est possible d'utiliser
MediaRecorder
sans créer d'abord un aperçu de la caméra et ignorer les premières étapes de ce processus. Cependant, comme les utilisateurs préfèrent généralement voir un aperçu avant de commencer un enregistrement, ce processus n'est pas abordé ici.
Vous pouvez trouver les instructions étape par étape sur le lien ci-dessus. Après les instructions, il indiquera le devis que j'ai fourni ci-dessus.
En fait, c'est possible, mais vous devez simuler l'aperçu avec une SurfaceView factice
SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);
Mise à jour 21/09/11: Apparemment, cela ne fonctionne pas pour tous les appareils Android.
Faites d'abord fonctionner cela avant d'essayer de masquer l'aperçu.
SurfaceView
(compatibilité pré-Android-4.0) ou SurfaceTexture
(Android 4+, peut être rendu transparent)SurfaceView
SurfaceHolder
(via getHolder()
) signale surfaceCreated()
ou le TextureView
signale onSurfaceTextureAvailable
à son SurfaceTextureListener
avant de définir et d'initialiser l'aperçu.WindowManager
MATCH_PARENT
X MATCH_PARENT
pour tester)View.VISIBLE
(qui semble être la valeur par défaut si vous ne le spécifiez pas)FLAG_HARDWARE_ACCELERATED
dans le LayoutParams
s'il s'agit d'un TextureView
.takePicture
car la documentation indique que les autres rappels ne sont pas pris en charge sur tous les appareilssurfaceCreated
/onSurfaceTextureAvailable
n'est pas appelé, le SurfaceView
/TextureView
n'est probablement pas affiché.takePicture
échoue, assurez-vous d'abord que l'aperçu fonctionne correctement. Vous pouvez supprimer votre appel takePicture
et laisser l'aperçu s'exécuter pour voir s'il s'affiche à l'écran.takePicture
afin que l'appareil photo ait le temps d'ajuster son exposition une fois l'aperçu lancé.Faites l'aperçu View
Taille 1x1 pour minimiser sa visibilité ( ou essayez 8x16 pour éventuellement plus de fiabilité )
new WindowManager.LayoutParams(1, 1, /*...*/)
Déplacez l'aperçu hors du centre pour réduire sa visibilité:
new WindowManager.LayoutParams(width, height,
Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
Rendre l'aperçu transparent (ne fonctionne que pour TextureView
)
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
width, height, /*...*/
PixelFormat.TRANSPARENT);
params.alpha = 0;
/** 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; }
}
Sur Android 4.0 et supérieur (niveau API> = 14), vous pouvez utiliser TextureView pour prévisualiser le flux de la caméra et le rendre invisible afin de ne pas le montrer au utilisateur. Voici comment:
Créez d'abord une classe pour implémenter un SurfaceTextureListener qui obtiendra les rappels de création/mise à jour pour la surface d'aperçu. Cette classe prend également un objet caméra en entrée, afin qu'il puisse appeler la fonction startPreview de la caméra dès que la surface est créée:
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!
}
}
Vous devrez également implémenter une classe de rappel pour traiter les données d'aperçu:
public class CamCallback implements Camera.PreviewCallback{
public void onPreviewFrame(byte[] data, Camera camera){
// Process the camera data here
}
}
Utilisez les classes CamPreview et CamCallback ci-dessus pour configurer la caméra dans la fonction de démarrage onCreate () ou similaire de votre activité:
// 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);
Il existe un moyen de le faire, mais c'est un peu délicat. ce qui devrait être fait, c'est d'attacher un support de surface au gestionnaire de fenêtres du service
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);
puis définissez
surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);
où mHolder est le support que vous obtenez de la vue de surface.
de cette façon, vous pouvez jouer avec l'alpha de la vue de surface, le rendre complètement transparent, mais la caméra obtiendra toujours des images.
c'est comme ça que je le fais. J'espère que ça aide :)
Nous avons résolu ce problème en utilisant un SurfaceView factice (non ajouté à l'interface graphique réelle) dans les versions inférieures à 3.0 (ou disons que 4.0, car un service de caméra sur une tablette n'a pas vraiment de sens). Dans les versions> = 4.0, cela ne fonctionnait que dans l'émulateur; (L'utilisation de SurfaceTexture (et setSurfaceTexture ()) au lieu de SurfaceView (et setSurfaceView ()) fonctionnait ici. Au moins, cela fonctionne sur Nexus S.
Je pense que c'est vraiment une lacune du framework Android.
Dans le "Exemple de travail de Sam" (Merci Sam ...)
if at istruction "wm.addView (preview, params);"
obtenir l'exception "Impossible d'ajouter la fenêtre Android.view.ViewRoot - autorisation refusée pour ce type de fenêtre"
résoudre en utilisant cette autorisation dans AndroidManifest:
<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW"/>
Vous pouvez essayer ce code de travail, ce service cliquez sur l'image avant, si vous voulez capturer l'image de la caméra arrière, puis décommentez backCamera dans le code et commentez frontCamera.
Remarque: - Autorisez la caméra et le stockage sur l'application et démarrez le service depuis l'activité ou n'importe où.
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();
}
}
}