web-dev-qa-db-ja.com

プレビューなしでカメラから写真を撮る

私はAndroid 1.5アプリケーションを起動直後に起動します。これはServiceであり、プレビューなしで写真を撮る必要があります。このアプリは、私は写真を撮ることができましたが、写真は黒でした。

長い間研究した後、私はそれについてバグスレッドに出会いました。プレビューを生成しない場合、Androidカメラは露出と焦点を設定するためにプレビューが必要です。SurfaceViewとリスナーを作成しましたが、 onSurfaceCreated()イベントは発生しません。

その理由は、表面が視覚的に作成されていないためだと思います。また、_MediaStore.CAPTURE_OR_SOMETHING_を使用して静的にカメラを呼び出す例をいくつか見てきました。これは写真を撮り、2行のコードで目的のフォルダーに保存しますが、写真も撮りません。

この関数を呼び出すためにIPCおよびbindService()_を使用する必要がありますか?または、これを実現する代替方法はありますか?

57
eyurdakul

Androidプラットフォームは、有効なプレビューサーフェスが与えられるまでビデオをストリーミングできません。プラットフォームの設計者は、サードパーティのビデオストリーミングアプリケーションについてまったく考えていなかったようです。拡張現実の場合でも、リアルタイムのカメラストリームではなく、何らかの視覚的な代替として画像を表示できます。

とにかく、単純にプレビューサーフェスのサイズを1x1ピクセルに変更にして、ウィジェット(視覚要素)の隅のどこかに置くことができます。注意してください-カメラのフレームサイズではなく、プレビュー面のサイズを変更してください。

もちろん、このようなトリックは、システムリソースとバッテリーを消費する不要なデータストリーミング(プレビュー用)を排除しません。

49

Android Camera Docs でこれに対する答えを見つけました。

注:最初にカメラプレビューを作成せずにMediaRecorderを使用し、このプロセスの最初の数ステップをスキップすることが可能です。ただし、ユーザーは通常、記録を開始する前にプレビューを表示することを好むため、このプロセスについてはここでは説明しません。

上記のリンクで手順を追って説明しています。指示の後、それは私が上で提供した引用を述べます。

38

実際には可能ですが、ダミーのSurfaceViewでプレビューを偽造する必要があります

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

pdate 9/21/11:どうやらこれはすべてのAndroidデバイスに対して機能しません。

36
Frank

写真を撮る

プレビューを非表示にする前に、最初にこれを機能させてください。

  • プレビューを正しく設定します
    • SurfaceView(Android-4.0以前の互換性)またはSurfaceTexture(Android 4以降、透明にすることができます)を使用します
    • 写真を撮る前に設定して初期化する
    • SurfaceViewSurfaceHolderが(getHolder()を介して)surfaceCreated()を報告するのを待つか、TextureViewonSurfaceTextureAvailableを報告するのを待ちますSurfaceTextureListenerプレビューを設定および初期化する前。
  • プレビューが表示されることを確認します:
    • WindowManagerに追加します
    • レイアウトサイズが少なくとも1x1ピクセルであることを確認します(テストのためにMATCH_PARENT x MATCH_PARENTにすることから始めることをお勧めします)
    • 可視性がView.VISIBLEであることを確認します(指定しない場合はデフォルトのようです)
    • LayoutParamsの場合は、TextureViewFLAG_HARDWARE_ACCELERATEDを使用するようにしてください。
  • ドキュメントには、他のコールバックはすべてのデバイスでサポートされているわけではないと書かれているため、takePictureのJPEGコールバックを使用します

トラブルシューティング

  • surfaceCreated/onSurfaceTextureAvailableが呼び出されない場合、SurfaceView/TextureViewはおそらく表示されていません。
  • takePictureが失敗した場合、最初にプレビューが正しく機能していることを確認してください。 takePicture呼び出しを削除し、プレビューを実行して、画面に表示されるかどうかを確認できます。
  • 画像が本来よりも暗い場合は、takePictureを呼び出す前に約1秒間遅延して、プレビューが開始されたらカメラが露出を調整できるようにする必要があります。

プレビューを非表示にする

  • プレビューをView 1x1サイズにして、可視性を最小限に抑えます( または、信頼性を高めるために8x16を試してください

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • プレビューを中央から移動して、目立たないようにします。

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • プレビューを透明にします(TextureViewでのみ機能します)

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

作業例(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; }
}
34
Sam

Android 4.0以降(APIレベル> = 14)では、 TextureView を使用してカメラストリームをプレビューし、非表示にして、表示されないようにすることができます。ユーザーです。方法は次のとおりです。

最初に、プレビューサーフェスの作成/更新コールバックを取得するSurfaceTextureListenerを実装するクラスを作成します。このクラスは入力としてカメラオブジェクトも受け取るため、サーフェスが作成されるとすぐにカメラのstartPreview関数を呼び出すことができます。

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!
  }
}

また、プレビューデータを処理するコールバッククラスを実装する必要があります。

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

上記のCamPreviewクラスとCamCallbackクラスを使用して、アクティビティのonCreate()または同様のスタートアップ関数でカメラをセットアップします。

// 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);
21
Varun Gulshan

これを行う方法はありますが、ややトリッキーです。行うべきことは、サービスからウィンドウマネージャーにSurfaceholderをアタッチすることです

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);

そして、設定

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

ここで、mHolderは表面ビューから取得するホルダーです。

このようにして、Surfaceviewのアルファを完全に透明にすることができますが、カメラはまだフレームを取得します。

それは私がそれをする方法です。それが役に立てば幸い :)

20
Vlad

3.0より前のバージョンでダミーのSurfaceView(実際のGUIに追加されていない)を使用することでこの問題を解決しました(または、タブレットのカメラサービスとしての4.0は実際には意味がありません)。 4.0以降のバージョンでは、これはエミュレーターでのみ機能しました;(SurfaceView(およびsetSurfaceView())の代わりにSurfaceTexture(およびsetSurfaceTexture())の使用はここで機能しました。少なくともこれはNexus Sで機能します。

これはAndroidフレームワークの欠点だと思います。

13
mnl

「Samによる実例」(Thank you Sam ...)

istructionで「wm.addView(preview、params);」の場合

例外「ウィンドウAndroid.view.ViewRootを追加できません-このウィンドウタイプの許可は拒否されました」を取得

androidManifestでこの権限を使用して解決します。

<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW"/>
3
Giulio

この作業コードを試すことができます。このサービスは、フロントカメラの画像をクリックします。バックカメラの画像をキャプチャし、コードでbackCameraのコメントを解除し、frontCameraをコメントします。

注:-アクティビティまたは任意の場所からApp And startServiceにカメラとストレージのアクセス許可を許可します。

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();
            }
    }
}
1
kdblue